2016-03-24 16 views
4

Wie lösche ich manuell eine Instanz einer Klasse?Wie lösche ich manuell eine Instanz einer Klasse?

Beispiel:

#include <iostream> 
#include <cstring> 

class Cheese { 
private: 
    string brand; 
    float cost; 
public: 
    Cheese(); // Default constructor 
    Cheese(string brand, float cost); // Parametrized constructor 
    Cheese(const Cheese & rhs); // Copy construtor 
    ~Cheese(); // Destructor 
    // etc... other useful stuff follows 
} 

int main() { 
    Cheese cheddar("Cabot Clothbound", 8.99); 
    Cheese swiss("Jarlsberg", 4.99); 

    whack swiss; 
    // fairly certain that "whack" is not a keyword, 
    // but I am trying to make a point. Trash this instance! 

    Cheese swiss("Gruyère",5.99); 
    // re-instantiate swiss 

    cout << "\n\n"; 
    return 0; 
} 
+2

Wie ist "Cabot Clothbound" Cheddar Käse - Kommt aus dem Nichts in der Nähe [Cheddar] (https://en.wikipedia.org/wiki/Cheddar,_Somerset) –

+0

Eine Variable (Instanz einer Klasse) auf dem Stapel deklariert werden kann habe seinen Speicher mit '' memset'' auf Null gesetzt (was in manchen Fällen nützlich wäre, denke ich ...), aber sein Speicher kann erst am Ende des Funktionsumfangs freigegeben werden, in dem du ihn deklariert hast (bis zum Ende deines Haupt hier). Sie können Ihren variablen Inhalt einfach mit dem Kopierkonstruktor überschreiben, wenn Sie wirklich auf dem Stapel bleiben wollen, aber warum verwenden Sie nicht den Heap und die Zeiger in diesem Fall, in dem Sie Ihre Instanz "löschen" können? – lordjohncena

+0

@Ed: https://en.wikipedia.org/wiki/Cheddar_cheese –

Antwort

4

Eine Scoping verwenden können, damit Sie eine andere Instanz einer Klasse definieren.

Cheese swiss("Toe", 3.14) 

{ 
    Cheese swiss("Ear", 15.9); 
} 

Als eine allgemeine Regel werden lokal deklarierte Instanzen sich selbst zerstören, wenn sie den Gültigkeitsbereich verlassen.

Wenn Sie wirklich das Bedürfnis verspüren, Käse zu zerstören, müssen Sie es stattdessen dynamisch zuweisen.

Cheese *swiss = new Cheese("toe", 3); 

    // do something with swiss. 

    delete swiss; // throw it away. 

    swiss = new Cheese("Ear", 7); 

    // do something with swiss. 

    delete swiss; // throw it away. 

Dynamisch zugeordneter Speicher muss immer manuell gelöscht werden.

+1

_ "Als eine allgemeine Regel" _ Lies: absolut immer, ohne Ausnahmen, das bedeutet, dass out of scope bedeutet. –

+1

_ "Dynamisch zugeordneter Speicher muss immer manuell gelöscht werden" _ Das war vor 20 Jahren, aber jetzt, da du Smart Pointer verwenden solltest, ist es im Wesentlichen _never_ true in good code –

+0

Ich liebe deinen Nick. Ich stimme dir zu. Das Problem ist, dass die OP neu ist oder er würde keine Frage wie diese stellen. Die Antwort zielt darauf ab, ihn auf zwei Dinge hinzuweisen, die er wissen muss Die Antwort ist bereits komplex genug, ohne das Konzept der intelligenten Zeiger oder der Ausnahmebehandlung, die erforderlich ist, um diesen Code korrekt zu behandeln, hinzuzufügen – EvilTeach

8

Ohne den Anwendungsfall zu wissen, oder das eigentliche Problem, das Sie lösen wollen (bitte über the XY problem, Ihre Frage lesen ist ein gutes Beispiel dafür) der einfachste Weg, nur ist die Neuzuweisung:

Cheese swiss("Jarlsberg", 4.99); 
... 
swiss = Cheese("Gruyère",5.99); 

Das könnte von Natürlich müssen Sie einen Zuweisungsoperator implementieren, aber nach dem rules of three or five sollten Sie das trotzdem tun (aber der Zuweisungsoperator wird nicht benötigt, wenn Sie the rule of zero folgen).

Sie könnte verwenden auch Zeiger, wenn Sie explizit die aktuelle swiss Objekt zerstören wollen:

Cheese* swiss = new Cheese("Jarlsberg", 4.99); 
... 
delete swiss; 
swiss = new Cheese("Gruyère",5.99); 

Aber Zeiger ist eine Dose Würmer, die Sie vermeiden sollten, und nicht wirklich brauchen viel in modernes C++. Aber Zeiger (oder Referenzen) werden benötigt, wenn Sie Polymorphie wollen. Dann könnten Sie einen Zeiger auf die Basisklasse haben, die auf die tatsächliche Instanz zeigt, und Dinge wie virtuelle Funktionen werden wie erwartet funktionieren.

Auch und je nach Ihrer Situation, die wir noch nichts wissen, könnte man natürlich Verwendung Scoping:

Cheese swiss("Jarlsberg", 4.99); 
... 
{ 
    Cheese swiss("Gruyère",5.99); 
    // In here the swiss cheese is a Gruyère 
    ... 
} 
// Out here the swiss cheese is a Jarlsberg 

Obwohl Shadowing Variablennamen wie dies funktioniert, ist es eine schlechte Gewohnheit, die Sie, wie es vermeiden sollten fügt Verwirrung für Leser des Codes hinzu. Auf der anderen Seite hindert Sie auch bei der Verwendung von Scopes nichts daran, einen beliebigen (gültigen) Variablennamen zu verwenden, so dass Sie die äußere Bereichsinstanz jarlsberg und die innere Bereichsinstanz gruyere benennen könnten, wobei das gruyere Objekt dann am Ende zerstört würde Der Bereich würde genau wie jede andere verschachtelte Scope-Variable zerstört und "verschwinden".

+0

Der Name Shadowing ist eine schlechte Idee, ja, aber Sie könnten deutlicher machen, dass die allgemeine Technik der Verwendung von Klammern zur Kontrolle des Umfangs bei weitem die _best_ der drei gezeigten Techniken ist. –

+0

Separate Bereiche wären die besten, aber ich würde sequentielle Bereiche, nicht verschachtelte Bereiche, fördern: '{Cheese swiss (1); } ... {Cheese swiss (2); } ' – MSalters

+0

Hilfreiche Vorschläge, danke. Muss ich daraus schließen, dass diese Funktionalität in der Sprache nicht existiert, wie im OP angegeben? Das ist: '' whack swiss; '' – kmiklas

2

Es gibt einige wenige Fälle, in denen Sie dies tun müssten. Sie können jedoch möglicherweise einen abstrakten Datentyp erstellen.

Wenn Sie beispielsweise einen Variant-Typ erstellen, möchten Sie wahrscheinlich einen ausgerichteten Datentyp einrichten und dann manuell neu platzieren und löschen.

typename std::aligned_union<0, FirstType, RestTypes...>::type m_buffer; 

beleben:

new (&m_buffer) AssignType(forward<T>(x)); 

zu löschen:

(HeldType*)(&m_buffer)->~HeldType(); 

jedoch, wie in den zahlreichen anderen Beiträgen erwähnt. Wenn Sie normal programmieren, müssen Sie sich keine Gedanken darüber machen, dtors manuell aufzurufen. Wenn es auf dem Stapel ist, dann ist es für Sie aufgeräumt. Wenn es auf dem Haufen ist, dann wird delete sich darum kümmern. Der einzige Zeitpunkt, an dem Sie dies tun möchten, ist, dass Sie die Lebensdauer von Objekten manuell steuern. Der Hauptgrund, warum Sie dies tun möchten, ist, wenn Sie einen abstrakten Datentyp implementieren.

+0

Gute Antwort außer dem "Stack"/"Heap" gubbins –

+0

Gibt es eine Nomenklatur, die bevorzugt wird? – OmnipotentEntity

0

Die dunkle Seite von C++ verfügt über eine Technik, mit der Sie genau das ausführen können, was Sie im Originalbeitrag beschrieben haben. Ich weiß nicht, warum Sie das machen wollen - vielleicht ist die Implementierung eines Zuweisungsoperators mühsam. Vielleicht hast du den morbiden Wunsch etwas Unnatürliches zu deinem Programm zu machen. Ich kann Ihnen versichern, dass keine "normale" Person in Betracht ziehen würde, den Code zu verwenden, den ich unten offengelegt habe, zumindest nicht, wenn andere zusehen. Warum stelle ich das hier her?

Weil ich sadistisch bin.

Ich rufe die Macht der Community Wiki auf, mich vor dem Ansturm zu schützen!

#include <iostream> 
#include <string> 
#include <new> 

template <typename T, typename ...As> 
inline void Reconstruct(T &ojt, const As&... ctor_args) { 
    // Explicitly call the destructor, to destroy the object 
    // without doing anything to its memory allocation. 
    ojt.~T(); 

    // Use Placement new to call a constructor. 
    // Instead of allocating memory somewhere for a new object, 
    // this specifies where the new object will be constructed -- 
    // given here as the location of the object's previous 
    // incarnation. 
    // Also pass any arguments to the constructor. I forced 
    // these arguments to be const references in order to 
    // avoid extra copying, so "move" construction won't work 
    // and steps might need to be taken to accommodate other, 
    // more unusual constructors. 
    new(&ojt) T(ctor_args...); 
} 

class Cheese { 
    std::string brand; 
    float  cost; 
public: 
    Cheese() : cost(0) {} 
    Cheese(std::string brand, float cost) : brand(brand), cost(cost) {} 
    Cheese(const Cheese & rhs) = default; 
    ~Cheese() = default; 
    friend std::ostream& operator << (std::ostream& os, const Cheese& chz) { 
     return os << "[brand:\"" << chz.brand << "\" cost:" << chz.cost << ']'; 
    } 
}; 

int main() { 
    Cheese cheese, fromage; 
    std::cout << "cheese = " << cheese << '\n'; 

    Reconstruct(cheese, "Cabot Clothbound", 8.99f); 
    std::cout << "cheese = " << cheese << '\n'; 

    Reconstruct(fromage, cheese); 
    std::cout << "fromage = " << fromage << '\n'; 

    Reconstruct(cheese, "Jarlsberg", 4.99f); 
    std::cout << "cheese = " << cheese << '\n'; 
} 
0

Wenn Sie aus irgendeinem Grund nicht mit dem Zuweisungsoperator verwenden können, können Sie eine optional verwenden.

std::experimental::optional<Cheese> swiss(std::experimental::in_place, "Jarlsberg", 4.99); 

swiss = std::experimental::nullopt; // Calls Cheese::~Cheese internally 

// re-instantiate swiss 
swiss.emplace("Gruyère",5.99); 

Solange Sie nicht die optionalen speichern, können Sie wahrscheinlich auf dem Compiler angewiesen, um die zusätzliche interne Bool Optimierung aus.

Verwandte Themen