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.