Respostas:
Basicamente, sempre que você desejar que outra classe seja responsável pelo ciclo de vida dos objetos da sua classe ou se tiver motivos para impedir a destruição de um objeto, poderá tornar o destruidor privado.
Por exemplo, se você estiver fazendo algum tipo de contagem de referência, você pode ter o objeto (ou gerente "amigo") responsável por contar o número de referências a si mesmo e excluí-lo quando o número chegar a zero. Um dtor particular impediria que alguém o excluísse quando ainda houvesse referências a ele.
Para outra instância, e se você tiver um objeto que tenha um gerente (ou ele próprio) que possa destruí-lo ou que possa recusá-lo, dependendo de outras condições do programa, como uma conexão com o banco de dados sendo aberta ou um arquivo sendo gravado. Você pode ter um método "request_delete" na classe ou no gerente que verificará essa condição e ela será excluída ou recusada e retornará um status informando o que foi feito. Isso é muito mais flexível do que apenas chamar "excluir".
Esse objeto nunca pode ser criado na pilha. Sempre na pilha. E a exclusão deve ser feita através de um amigo ou membro. Um produto pode usar uma única hierarquia de objetos e um gerenciador de memória personalizado - esses cenários podem usar um dtor particular.
#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;
}
Quando você não deseja que os usuários acessem o destruidor, ou seja, deseja que o objeto seja destruído apenas por outros meios.
http://blogs.msdn.com/larryosterman/archive/2005/07/01/434684.aspx fornece um exemplo, em que o objeto é contado por referência e só deve ser destruído pelo próprio objeto quando a contagem for zero.
COM usa essa estratégia para excluir a instância. O COM torna o destruidor privado e fornece uma interface para excluir a instância.
Aqui está um exemplo de como seria um método Release.
int MyRefCountedObject::Release()
{
_refCount--;
if ( 0 == _refCount )
{
delete this;
return 0;
}
return _refCount;
}
Objetos ATL COM são um excelente exemplo desse padrão.
Adicionando as respostas já presentes aqui; construtores e destruidores privados são bastante úteis durante a implementação de uma fábrica onde os objetos criados precisam ser alocados no heap. Os objetos seriam, em geral, criados / excluídos por um membro ou amigo estático. Exemplo de uso típico:
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
}
A classe só pode ser excluída por si mesma. Útil se você estiver criando alguma tentativa de objeto contado de referência. Somente o método de liberação pode excluir o objeto, possivelmente ajudando a evitar erros.
Eu sei que você estava perguntando sobre destruidor particular. Aqui está como eu uso os protegidos. A idéia é que você não deseja excluir a classe principal através do ponteiro para a classe que adiciona funcionalidade extra à principal.
No exemplo abaixo, não quero que o GuiWindow seja excluído por meio de um ponteiro 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 está errado. Aqui está um exemplo de objeto com c-tor e d-tor privados criados na pilha (estou usando a função membro estática aqui, mas isso pode ser feito com a função friend ou a classe friend também).
#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);
};
Este código produzirá saída: dentro de PrivateCD :: TryMe, p._i = 8