Qual é a utilidade de ter o destruidor como privado?


Respostas:


176

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".


73

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

19
Correção: Esse objeto pode ser criado na pilha (mas apenas no escopo de um amigo ou ele próprio).
— Thomas Eding

Além disso, ele não pode ser um objeto estático ou global (por exemplo, ter "duração de armazenamento estático") em uma implementação hospedada (porque o destruidor seria chamado na saída do programa).
— Peter - Restabelece Monica


17

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.


8

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
}

7

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.


3

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(){}
};

3

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


3
Tenho certeza de que dirkgently quis dizer que o código que usa sua classe não pode instanciar a classe na pilha. É claro que você ainda pode instanciar a classe na pilha dentro dos métodos da classe, pois nesse contexto você pode acessar os memebers particulares.
— Edward Loper

2

Pode ser uma maneira de lidar com o problema no Windows, em que cada módulo pode usar um heap diferente, como o heap de depuração . Se esse problema não for tratado corretamente, coisas ruins podem acontecer.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.