¿De qué sirve tener destructor como privado?


Respuestas:


176

Básicamente, cada vez que desee que otra clase sea responsable del ciclo de vida de los objetos de su clase, o tenga razones para evitar la destrucción de un objeto, puede hacer que el destructor sea privado.

Por ejemplo, si está haciendo algún tipo de conteo de referencias, puede hacer que el objeto (o administrador que ha sido "amigo") sea responsable de contar el número de referencias a sí mismo y eliminarlo cuando el número llegue a cero. Un dtor privado evitaría que alguien más lo elimine cuando todavía hay referencias a él.

Para otra instancia, ¿qué pasa si tiene un objeto que tiene un administrador (o sí mismo) que puede destruirlo o puede negarse a destruirlo dependiendo de otras condiciones en el programa, como una conexión de base de datos abierta o un archivo escrito? Podría tener un método "request_delete" en la clase o en el administrador que verificará esa condición y eliminará o rechazará, y devolverá un estado que le indica lo que hizo. Eso es mucho más flexible que simplemente llamar "eliminar".


73

Tal objeto nunca se puede crear en la pila. Siempre en el montón. Y la eliminación debe hacerse a través de un amigo o miembro. Un producto puede usar una única jerarquía de Objetos y un administrador de memoria personalizado; estos escenarios pueden usar un dtor privado.

#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
Corrección: dicho objeto se puede crear en la pila (pero solo en el ámbito de un amigo o de sí mismo).
Thomas Eding

Además, no puede bañar un objeto estático o global (es decir, tener "duración de almacenamiento estático") en una implementación alojada (porque el destructor se invocaría al salir del programa).
Peter - Restablece a Monica


17

COM utiliza esta estrategia para eliminar la instancia. COM hace que el destructor sea privado y proporciona una interfaz para eliminar la instancia.

Aquí hay un ejemplo de cómo se vería un método de lanzamiento.

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

Los objetos ATL COM son un excelente ejemplo de este patrón.


8

Agregando a las respuestas ya presentes aquí; Los constructores y destructores privados son bastante útiles al implementar una fábrica donde los objetos creados deben asignarse en el montón. Los objetos, en general, serían creados / eliminados por un miembro estático o amigo. Ejemplo de un 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

La clase solo se puede eliminar por sí misma. Útil si está creando algún intento de referencia de objeto contado. Entonces solo el método de lanzamiento puede eliminar el objeto, posiblemente ayudándole a evitar errores.


3

Sé que estabas preguntando por el destructor privado. Así es como uso los protegidos. La idea es que no desea eliminar la clase principal a través del puntero a la clase que agrega funcionalidad adicional a la principal.
En el siguiente ejemplo, no quiero que GuiWindow se elimine a través de un puntero 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á mal. Aquí hay un ejemplo de objeto con c-tor privado y d-tor creado en la pila (estoy usando la función miembro estática aquí, pero también se puede hacer con la función amigo o la clase amigo).

#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 producirá salida: dentro de PrivateCD :: TryMe, p._i = 8


3
Estoy bastante seguro de que dirkgentgently quiso decir que el código que usa su clase no puede instanciar la clase en la pila. Por supuesto, aún puede crear instancias de la clase en la pila dentro de los métodos de clase, ya que en ese contexto puede acceder a miembros privados.
Edward Loper

2

Podría ser una forma de tratar el problema en Windows, donde cada módulo puede usar un montón diferente, como el montón de depuración . Si ese problema no se maneja correctamente , pueden suceder cosas malas .

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.