Was nützt es, Destruktor als privat zu haben?


Antworten:


176

Grundsätzlich können Sie den Destruktor jederzeit privat machen, wenn Sie möchten, dass eine andere Klasse für den Lebenszyklus der Objekte Ihrer Klasse verantwortlich ist, oder wenn Sie Grund haben, die Zerstörung eines Objekts zu verhindern.

Wenn Sie beispielsweise eine Art Referenzzählung durchführen, kann das Objekt (oder der Manager, der "befreundet" wurde) dafür verantwortlich sein, die Anzahl der Referenzen auf sich selbst zu zählen und sie zu löschen, wenn die Zahl Null erreicht. Ein privater dtor würde verhindern, dass andere Personen es löschen, wenn noch Verweise darauf vorhanden sind.

In einem anderen Fall, was ist, wenn Sie ein Objekt haben, das einen Manager (oder sich selbst) hat, der es zerstören oder ablehnen kann, es zu zerstören, abhängig von anderen Bedingungen im Programm, z. B. wenn eine Datenbankverbindung geöffnet ist oder eine Datei geschrieben wird. Sie könnten eine "request_delete" -Methode in der Klasse oder im Manager haben, die diese Bedingung überprüft und sie entweder löscht oder ablehnt und einen Status zurückgibt, der Ihnen sagt, was sie getan hat. Das ist weitaus flexibler als nur "Löschen" zu nennen.


73

Ein solches Objekt kann niemals auf dem Stapel erstellt werden. Immer auf dem Haufen. Das Löschen muss über einen Freund oder ein Mitglied erfolgen. Ein Produkt kann eine einzelne Objekthierarchie und einen benutzerdefinierten Speichermanager verwenden. In solchen Szenarien wird möglicherweise ein privater dtor verwendet.

#include <iostream>
class a {
    ~a() {}
    friend void delete_a(a* p);
};


void delete_a(a* p)  {
    delete p;
}

int main()
{
    a *p = new a;
    delete_a(p);

    return 0;
}

19
Korrektur: Ein solches Objekt kann auf dem Stapel erstellt werden (jedoch nur im Bereich eines Freundes oder sich selbst).
Thomas Eding

Außerdem kann es in einer gehosteten Implementierung kein statisches oder globales Objekt (dh "statische Speicherdauer") haben (da der Destruktor beim Beenden des Programms aufgerufen würde).
Peter - Monica


17

COM verwendet diese Strategie zum Löschen der Instanz. COM macht den Destruktor privat und bietet eine Schnittstelle zum Löschen der Instanz.

Hier ist ein Beispiel dafür, wie eine Release-Methode aussehen würde.

int MyRefCountedObject::Release() 
{
 _refCount--;
 if ( 0 == _refCount ) 
 {
    delete this;
    return 0;
 }
 return _refCount;
}

ATL COM-Objekte sind ein Paradebeispiel für dieses Muster.


8

Hinzufügen zu den hier bereits vorhandenen Antworten; Private Konstruktoren und Destruktoren sind sehr nützlich bei der Implementierung einer Factory, in der die erstellten Objekte auf dem Heap zugewiesen werden müssen. Die Objekte werden im Allgemeinen von einem statischen Mitglied oder Freund erstellt / gelöscht. Beispiel einer typischen Verwendung:

class myclass
{
public:
    static myclass* create(/* args */)  // Factory
    {
        return new myclass(/* args */);
    }

    static void destroy(myclass* ptr)
    {
        delete ptr;
    }
private:
    myclass(/* args */) { ... }         // Private CTOR and DTOR
    ~myclass() { ... }                  // 
}

int main ()
{
    myclass m;                          // error: ctor and dtor are private
    myclass* mp = new myclass (..);     // error: private ctor
    myclass* mp = myclass::create(..);  // OK
    delete mp;                          // error: private dtor
    myclass::destroy(mp);               // OK
}

7

Die Klasse kann nur von selbst gelöscht werden. Nützlich, wenn Sie ein Objekt mit Referenzzählung erstellen. Dann kann nur die Freigabemethode das Objekt löschen, wodurch Sie möglicherweise Fehler vermeiden können.


3

Ich weiß, dass Sie nach einem privaten Zerstörer gefragt haben. Hier ist, wie ich geschützte benutze. Die Idee ist, dass Sie die Hauptklasse nicht über den Zeiger auf die Klasse löschen möchten, die der Hauptklasse zusätzliche Funktionen hinzufügt.
Im folgenden Beispiel möchte ich nicht, dass GuiWindow über einen HandlerHolder-Zeiger gelöscht wird.

class Handler
{
public:
    virtual void onClose() = 0;
protected:
    virtual ~Handler();
};

class HandlerHolder
{
public:
    void setHandler( Handler* );
    Handler* getHandler() const;
protected:
    ~HandlerHolder(){}
private:
    Handler* handler_;
};

class GuiWindow : public HandlerHolder
{
public:
    void finish()
    {
        getHandler()->onClose();
    }

    virtual ~GuiWindow(){}
};

3

dirkgently ist falsch. Hier ist ein Beispiel für ein Objekt mit privatem C-Tor und D-Tor, das auf einem Stapel erstellt wurde (ich verwende hier die statische Elementfunktion, aber es kann auch mit der Friend-Funktion oder der Friend-Klasse ausgeführt werden).

#include <iostream>

class PrivateCD
{
private:
    PrivateCD(int i) : _i(i) {};
    ~PrivateCD(){};
    int _i;
public:
    static void TryMe(int i)
    {
        PrivateCD p(i);
        cout << "inside PrivateCD::TryMe, p._i = " << p._i << endl;
    };
};

int main()
{
    PrivateCD::TryMe(8);
};

Dieser Code erzeugt eine Ausgabe: in PrivateCD :: TryMe, p._i = 8


3
Ich bin mir ziemlich sicher, dass Code, der Ihre Klasse verwendet, die Klasse auf dem Stapel nicht instanziieren kann. Natürlich können Sie die Klasse auf dem Stapel innerhalb von Klassenmethoden instanziieren , da Sie in diesem Kontext auf private Mitglieder zugreifen können.
Edward Loper

2

Dies kann eine Möglichkeit sein, das Problem in Windows zu beheben, bei dem jedes Modul einen anderen Heap verwenden kann, z. B. den Debug- Heap. Wenn dieses Problem nicht richtig behandelt wird, können schlimme Dinge passieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.