2013-11-22 11 views
7

Um über Zeiger und Referenzen in this question zu erklären, schrieb ich diesen Code.* neu ist immer falsch. IMMER

MyClass& MyClass::MyInstance() 
{  
    static MyClass & myLocalVariable = * new MyClass(/*parameters*/); 
    return myLocalVariable ; 
} 

einer der Kommentare von einem wirklich beeindruckend hohen Ansehen SO Benutzer, einfach erklärt: * new immer falsch ist. IMMER.

Es ist das erste Mal, dass mir gesagt wird: Ist es ein berühmter Kodierungsstandard, den wir alle kennen sollten? Was sind die Gründe dafür?

+7

Wie genau wird das gelöscht? – Nim

+0

In der origianl Frage habe ich auch über die Verwendung eines std :: shared_ptr statt eines Vanille-Pointer –

+2

erzählt Wenn Sie 'neu', müssen Sie' löschen'. In diesem Fall können Sie nicht. – crashmstr

Antwort

9

Ich bin normalerweise pragmatisch, aber das ist selbst für mich zu viel!

static MyClass & myLocalVariable = * new MyClass(/*parameters*/); 

Ernsthaft? Warum nicht einfach:

static MyClass myLocalVariable{/*parameters*/}; 
+1

Das ist der Punkt ... Wenn du ein Singleton machst, brauchst du dieses "neue" Zeug nicht. – Johan

+0

Was passiert, wenn 'new' einen sehr ausgefallenen Zuordner verwendet, den Sie für dieses statische Objekt verwenden möchten? – RedX

+1

@RedX, warum möchten Sie einen schicken Allokator für ein Singleton verwenden? – Nim

3

Der offensichtlichste Grund ist, dass, wenn Sie nicht über eine Kopie des Zeigers halten die neue zurückgegeben, sind Sie wahrscheinlich nicht immer auf ihn löschen nennen.

Auf einer menschlichen Ebene, wird es Menschen Code denken das Lesen weniger von euch, und das ist nie eine gute Sache auch nicht.

+8

Es wird Menschen dazu bringen, Ihren Code zu lesen * wollen Sie umbringen *. FTFY – Thomas

+0

In diesem Fall können Sie den Zeiger von der Objektreferenz abrufen? (Nicht, dass dies ein guter Weg wäre, Dinge zu tun ...) – fjc

+1

@Frank - Ja, weil eine C++ - Referenz nur ein getarnter Zeiger ist, so dass Sie leicht auf den Zeiger zurückkommen können. Es ist immer noch eine schrecklich aussehende Sache. –

0

Ich glaube, der angegebene Benutzer meinte, dass die Zuweisung eines statischen Objekts mit neuen ist gefährlich, da der Speicher höchstwahrscheinlich geleakt wird. Noch wichtiger ist, dass Ihre Variable kein Zeiger, sondern eine Referenz ist, so dass die Chance, dass Sie niemals den von new zurückgegebenen Speicher freigeben, noch größer ist (wie oft löschen Sie die Adresse einer Referenz?).

0

Das Problem ist mehr als nur nutzlose Zuordnungen.

Wenn niemand löscht, wird es nicht gelöscht. Sicher, der Speicher wird freigegeben, wenn das Programm endet, aber sein Destruktor nicht aufgerufen wird.

Vergleichen Sie die Ausgabe von unter Get() und Get2() im Code verwendet:

#include <iostream> 

    struct A 
    { 
     ~A(){std::cout << "Deleted\n";} 
    }; 

    A& Get() 
    { 
     static A & a = *new A; 
     return a; 
    } 

    A& Get2() 
    { 
     static A a; 
     return a; 
    } 

    int main() 
    { 
     //Case 1 
     Get(); 
     //Case 2 
     Get2(); 
    } 

Die Ausgabe dieses Programms ist nichts, wenn Get und Deleted Aufruf wenn Get2 aufrufen.
Mit anderen Worten, die Ressourcen mit nicht-trivialen Destruktoren (Commit-on-close Datei-Handelte zum Beispiel), falls nicht ordnungsgemäß bei Programmende wird zerstört 1, wird aber im Fall 2.

0

Das Problem besteht darin, dass ein rohes neu gibt kein Eigentumsrecht an. Wenn ich ein Objekt neu erstelle und es zurückgebe, wem gehört es? Besitzt die Erstellungsfunktion/das Objekt sie, oder funktioniert die aufrufende Funktion? Wenn Sie Smartpointer (std :: shared_ptr und std :: unique_ptr) zurückgeben, geben Sie den Besitz an.

Nicht spezifiziert Eigentum ist eine der einfachsten Möglichkeiten, Speicher zu verlieren. Ich habe die schwierigste Zeit, sogar mit professionellen Programmierern, Leute dazu zu bringen, Besitz zu verstehen und damit zu arbeiten. Dies wird meist durch die Verwendung von guten Typen (Smartpointern) verhindert, die den Besitz angeben, nur indem sie existieren.

type* function(); // Unspecified ownership. 
        // Must be well documented and all users must read 
        // and follow the documentation. 
std::unique_ptr<type> function(); // Calling function owns returned pointer. 
            // Single ownership. 
std::shared_ptr<type> function(); // Calling function owns returned pointer. 
            // Shared ownership. Can have multiple owners. 
std::weak_ptr<type> function(); // Calling function references returned pointer. 
            // Must lock pointer to get owned object, if not deleted. 
            // Shared ownership. Can have multiple owners. 

Diese verschiedenen Arten von Zeigern drücken Besitz nur durch existierende im Gegensatz zu rohen Zeigern aus.

Wie für new immer falsch. Das ist eine generelle Verallgemeinerung.std::shared_ptr wird mit der globalen Funktion std::make_shared erstellt. Ab C++ 11 gibt es keine std::make_unique, aber das wird in C++ 14 behoben werden. Die einzige Möglichkeit, ein std::unique_ptr zu erstellen, ist die Verwendung von new und sofort zuweisen Sie den Zeiger auf std::unique_ptr.

Es gibt auch Orte, wo Sie einen rohen Zeiger und manuell new und delete verwenden möchten, aber sie neigen dazu, sehr niedrigen Pegel zu sein, und die meisten Programmierer werden sie selten begegnen.


Was mir wirklich über Ihren Code kriechend hat, ist nicht, dass Sie new verwenden, aber dass Sie die Zeiger sind dereferencing und es zu einem Referenz zuweisen. Es wäre fast unmöglich zu garantieren, dass der Destruktor jemals aufgerufen wird. Es neigt auch dazu, Speicher zu verlieren, obwohl im Fall der Zuweisung zu einer statischen Variable wird es bei Programmbeendigung freigegeben werden, so dass Sie nicht wirklich Speicherleck suchen.

MyClass& MyClass::MyInstance() 
{  
    static MyClass & myLocalVariable = * new MyClass(/*parameters*/); 
    return myLocalVariable ; 
} 

Ich würde lieber erstellen, um die statische Variable nach Wert als durch Referenz zu haben. Dies verhindert, dass das Objekt auf den Heap gesetzt wird. Abhängig von MyClass ist es auch möglich, das Objekt aus der ausführbaren Datei in den Speicher zu mappen, ohne dass ein Code zur Initialisierung ausgeführt werden muss.

MyClass& MyClass::MyInstance() 
{ 
    static MyClass myLocalVariable(/*parameters*/); 
    return myLocalVariable ; 
} 
Verwandte Themen