Odpowiedzi:
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”.
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;
}
Gdy nie chcesz, aby użytkownicy mieli dostęp do destruktora, tzn. Chcesz, aby obiekt został zniszczony tylko w inny sposób.
http://blogs.msdn.com/larryosterman/archive/2005/07/01/434684.aspx podaje przykład, w którym obiekt jest liczony jako referencja i powinien zostać zniszczony przez sam obiekt, gdy liczba osiągnie zero.
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.
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
}
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.
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(){}
};
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
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 .