Po co mieć Destruktor jako prywatny?

Odpowiedzi:

176

Zasadniczo, za każdym razem, gdy chcesz, aby inna klasa była odpowiedzialna za cykl życia obiektów twojej klasy lub masz powód, aby zapobiec zniszczeniu obiektu, możesz ustawić prywatny destruktor na prywatny.

Na przykład, jeśli robisz coś z liczenia odniesień, możesz sprawić, że obiekt (lub menedżer, który został „przyjacielem”) jest odpowiedzialny za zliczanie liczby odniesień do siebie i usuwa go, gdy liczba osiągnie zero. Prywatny dtor zapobiegnie usunięciu go przez kogoś innego, gdy nadal będą do niego odniesienia.

Na przykład, jeśli masz obiekt, który ma menedżera (lub siebie), który może go zniszczyć lub może odmówić zniszczenia w zależności od innych warunków w programie, takich jak otwarte połączenie z bazą danych lub zapisywanie pliku. Możesz mieć w klasie metodę „request_delete” lub menedżera, która sprawdzi ten warunek i usunie lub odrzuci i zwróci status informujący o tym, co zrobił. Jest to o wiele bardziej elastyczne niż zwykłe „usuwanie”.

Paul Tomblin
źródło
73

Taki obiekt nigdy nie może zostać utworzony na stosie. Zawsze na kupie. Usunięcie musi zostać wykonane przez znajomego lub członka. Produkt może korzystać z jednej hierarchii obiektów i niestandardowego menedżera pamięci - takie scenariusze mogą wykorzystywać prywatny dtor.

#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;
}
bezpośrednio
źródło
19
Korekta: taki obiekt można utworzyć na stosie (ale tylko w zasięgu znajomego lub samego siebie).
Thomas Eding
Ponadto nie może udostępniać obiektu statycznego ani globalnego (tj. Mieć „statyczny czas przechowywania”) w hostowanej implementacji (ponieważ destruktor byłby wywoływany przy wyjściu z programu).
Peter - przywróć Monikę
17

COM używa tej strategii do usuwania instancji. COM sprawia, że ​​destruktor jest prywatny i zapewnia interfejs do usuwania instancji.

Oto przykład, jak wyglądałaby metoda Release.

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

Obiekty COM ATL są doskonałym przykładem tego wzorca.

Vinay
źródło
8

Dodanie do odpowiedzi tutaj już obecnych; Prywatne konstruktory i destruktory są bardzo przydatne podczas implementacji fabryki, w której tworzone obiekty muszą być przydzielane na stercie. Obiekty byłyby generalnie tworzone / usuwane przez statycznego członka lub przyjaciela. Przykład typowego zastosowania:

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
}
nav
źródło
7

Klasa może zostać usunięta tylko sama. Przydatne, jeśli tworzysz jakąś próbę odniesienia obiektu zliczonego. Wtedy tylko metoda wydania może usunąć obiekt, co może pomóc w uniknięciu błędów.

Roland Rabien
źródło
3

Wiem, że pytałeś o prywatny destruktor. Oto jak używam chronionych. Chodzi o to, że nie chcesz usuwać klasy głównej za pomocą wskaźnika do klasy, która dodaje dodatkową funkcjonalność do klasy głównej.
W poniższym przykładzie nie chcę, aby GuiWindow został usunięty za pomocą wskaźnika HandlerHolder.

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(){}
};
Mykoła Golubiew
źródło
3

dirkgently jest w błędzie. Oto przykład obiektu z prywatnym c-tor i d-tor utworzonym na stosie (używam tutaj statycznej funkcji członka, ale można to zrobić również za pomocą funkcji znajomego lub klasy przyjaciela).

#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);
};

Ten kod wygeneruje dane wyjściowe: wewnątrz PrivateCD :: TryMe, p._i = 8

misicd
źródło
3
Jestem pewien, że jednoznacznie oznaczało, że kod używający twojej klasy nie może utworzyć instancji klasy na stosie. Oczywiście nadal możesz tworzyć instancję klasy na stosie w ramach metod klasowych, ponieważ w tym kontekście masz dostęp do prywatnych członków.
Edward Loper
2

Może to być sposób na rozwiązanie problemu w systemie Windows, w którym każdy moduł może korzystać z innej sterty, na przykład sterty debugowania . Jeśli problem nie zostanie rozwiązany poprawnie , mogą się zdarzyć złe rzeczy .

Jared Oberhaus
źródło