14 Stimmen

Objekt wird bereits bei der Deklaration initialisiert?

Ich versuche etwas in C++ zu verstehen. Grundsätzlich habe ich das hier:

class SomeClass {
    public:
        SomeClass();
    private:
        int x;
};

SomeClass::SomeClass(){
    x = 10;
}

int main() {
    SomeClass sc;
    return 0;
}

Ich dachte, dass 'sc' eine nicht initialisierte Variable vom Typ SomeClass ist, aber in den verschiedenen Tutorials, die ich gefunden habe, sieht es so aus, als ob diese Deklaration tatsächlich eine Initialisierung ist, die den Konstruktor SomeClass() aufruft, ohne dass ich "sc = new SomeClass();" oder so etwas aufrufen muss.

Da ich aus der C# Welt komme (und ein wenig C kenne, aber kein C++), versuche ich zu verstehen, wann ich Dinge wie 'new' benötige und wann ich Objekte so freigeben muss. Ich habe ein Muster namens RAll gefunden, das scheinbar nichts damit zu tun hat.

Wie nennt man diese Art der Initialisierung und wie weiß ich, ob etwas nur eine Deklaration oder eine volle Initialisierung ist?

3voto

Stefano Borini Punkte 132313

Was Sie in der ersten Zeile von main() tun, ist die Zuweisung eines SomeClass-Objekts auf dem Stapel. Der new-Operator weist dagegen Objekte auf dem Heap zu und gibt einen Zeiger auf die Klasseninstanz zurück. Dies führt schließlich zu den beiden verschiedenen Zugriffstechniken über den . (mit der Instanz) oder mit dem -> (mit dem Zeiger)

Da Sie C kennen, führen Sie bei jedem Aufruf wie z. B. int i; eine Stapelspeicherzuweisung durch. Auf der anderen Seite wird in C die Heapspeicherzuweisung durch malloc() durchgeführt. malloc() gibt einen Zeiger auf einen neu zugewiesenen Speicherplatz zurück, der dann in einen Zeiger auf etwas umgewandelt wird. Beispiel:

int *i;
i = (int *)malloc(sizeof(int));
*i=5;

Während die Freigabe der auf dem Stapel zugewiesenen Dinge automatisch erfolgt, muss die Freigabe der auf dem Heap zugewiesenen Dinge durch den Programmierer erfolgen.

Die Quelle Ihrer Verwirrung kommt daher, dass C# (das ich nicht benutze, aber ich weiß, dass es ähnlich wie Java ist) keine Stapelzuweisung hat. Was Sie tun, wenn Sie SomeClass sc sagen, ist die Deklaration eines SomeClass-Verweises, der derzeit nicht initialisiert ist, bis Sie new sagen, was der Moment ist, in dem das Objekt entsteht. Vor dem new haben Sie kein Objekt. In C++ ist das nicht der Fall. Es gibt kein Konzept von Verweisen in C++, das ähnlich ist wie in C# (oder Java), obwohl Sie in C++ nur Referenzen während Funktionsaufrufen haben (es handelt sich um ein Pass-by-Reference-Paradigma in der Praxis. Standardmäßig gibt C++ Objekte beim Funktionsaufruf weiter). Das ist jedoch nicht die ganze Geschichte. Überprüfen Sie die Kommentare für genauere Details.

2voto

TreDubZedd Punkte 2493

In Ihrem Fall wird sc auf dem Stapel allokiert und der Standardkonstruktor für SomeClass verwendet. Da es sich auf dem Stapel befindet, wird die Instanz beim Verlassen der Funktion destruiert. (Dies wäre beeindruckender, wenn Sie SomeClass sc innerhalb einer Funktion instanziieren würden, die von main aufgerufen wird - der für sc allokierte Speicher würde beim Zurückkehren zu main nicht mehr allokiert sein.)

Das Schlüsselwort new allokiert anstelle der Speicherung auf dem Laufzeitstapel den Speicher auf dem Heap. Da C++ keine automatische Garbage Collection hat, sind Sie (der Entwickler) dafür verantwortlich, den von Ihnen auf dem Heap allokierten Speicher zu deallokieren (unter Verwendung des Schlüsselworts delete), um Speicherlecks zu vermeiden.

2voto

CB Bailey Punkte 693084

Bei der Deklaration einer Variablen (ohne extern) im Funktionsbereich (z.B. in main) haben Sie auch die Variable definiert. Die Variable wird zum Zeitpunkt erreicht, an dem die Deklaration erreicht wird, und wird aufgehoben, wenn das Ende ihres Bereichs erreicht ist (in diesem Fall das Ende der Funktion main).

Wenn ein Objekt erstellt wird, wird, sofern es einen vom Benutzer deklarierten Konstruktor hat, einer seiner Konstruktoren verwendet, um es zu initialisieren. Ebenso wird, wenn es einen vom Benutzer deklarierten Destruktor hat, dieser verwendet, wenn das Objekt außerhalb des Bereichs liegt, um erforderliche Bereinigungsmaßnahmen durchzuführen zum Zeitpunkt, an dem es außerhalb des Bereichs liegt. Dies unterscheidet sich von Sprachen, die Finalizer haben können, die möglicherweise nicht ausgeführt werden und sicherlich nicht zu einem deterministischen Zeitpunkt. Es ist eher wie using / IDisposable.

Ein new-Ausdruck wird in C++ verwendet, um ein Objekt dynamisch zu erstellen. Es wird normalerweise dort verwendet, wo die Lebensdauer des Objekts nicht an einen bestimmten Bereich gebunden werden kann. Zum Beispiel, wenn es weiterhin existieren muss, nachdem die Funktion, die es erstellt hat, abgeschlossen ist. Es wird auch dort verwendet, wo der genaue Typ des zu erstellenden Objekts zum Compile-Zeitpunkt nicht bekannt ist, z.B. in einer Factory-Funktion. Dynamisch erstellte Objekte können oft in vielen Fällen vermieden werden, in denen sie in Sprachen wie Java und C# häufig verwendet werden.

Wenn ein Objekt mit new erstellt wird, muss es irgendwann durch einen delete-Ausdruck zerstört werden. Um sicherzustellen, dass Programmierer dies nicht vergessen, ist es üblich, eine Art von Smart-Pointer-Objekt einzusetzen, um dies automatisch zu verwalten, z.B. ein shared_ptr aus tr1 oder boost.

2voto

Greg Rogers Punkte 34400

Einige andere Antworten sagen im Grunde genommen "sc wird auf dem Stapel allokiert, new allokiert das Objekt auf dem Heap". Ich ziehe es vor, nicht auf diese Weise darüber nachzudenken, da es Implementierungsdetails (Stapel/Heap) mit der Semantik des Codes vermischt. Da du daran gewöhnt bist, wie Dinge in C# gemacht werden, denke ich, dass es auch Unklarheiten schafft. Stattdessen bevorzuge ich es, darüber nachzudenken, wie es im C++-Standard beschrieben wird:

sc ist eine Variable vom Typ SomeClass, die im Blockumfang deklariert ist (d. h. die Klammern, die die Hauptfunktion bilden). Dies wird als lokale Variable bezeichnet. Da sie nicht als static oder extern deklariert ist, hat sie automatische Speicherdauer. Was dies bedeutet ist, dass immer wenn die Zeile SomeClass sc; ausgeführt wird, die Variable initialisiert wird (indem ihr Konstruktor ausgeführt wird), und wenn die Variable den Block verlässt, wird sie zerstört (indem ihr Destruktor ausgeführt wird - da du nicht einen hast und dein Objekt einfach alte Daten sind, wird nichts getan).

Früher sagte ich "Da es nicht als static oder extern deklariert ist", wenn du es so deklariert hättest, hätte es statische Speicherdauer. Es würde vor dem Programmstart initialisiert werden (technisch gesehen würde es im Blockumfang beim ersten Gebrauch initialisiert werden), und nach Programmende zerstört werden.

Wenn du new verwendest, um ein Objekt zu erstellen, erstellst du ein Objekt mit dynamischer Speicherdauer. Dieses Objekt wird initialisiert, wenn du new aufrufst, und wird nur zerstört, wenn du delete darauf aufrufst. Um delete aufzurufen, musst du eine Referenz darauf aufrechterhalten und delete aufrufen, wenn du fertig bist, das Objekt zu verwenden. Gut geschriebener C++-Code verwendet typischerweise diese Art von Speicherdauer nicht sehr oft. Stattdessen legst du typischerweise Wertobjekte in Container (z. B. std::vector), die die Lebensdauer der enthaltenen Werte verwalten. Die Container-Variable selbst kann im statischen oder im automatischen Speicher platziert werden.

Hoffentlich hilft dir das, die Dinge ein wenig zu entwirren, ohne zu viele neue Begriffe zu verwenden, um dich zu verwirren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X