A che serve avere un distruttore come privato?


Risposte:


177

Fondamentalmente, ogni volta che vuoi che un'altra classe sia responsabile del ciclo di vita degli oggetti della tua classe, o hai motivo di impedire la distruzione di un oggetto, puoi rendere privato il distruttore.

Ad esempio, se stai facendo una sorta di cosa di conteggio dei riferimenti, puoi avere l'oggetto (o il manager che è stato "amico" ed) responsabile del conteggio del numero di riferimenti a se stesso ed eliminarlo quando il numero raggiunge lo zero. Un agente privato impedirebbe a chiunque altro di eliminarlo quando vi erano ancora riferimenti ad esso.

Per un'altra istanza, cosa succede se si dispone di un oggetto con un gestore (o se stesso) che potrebbe distruggerlo o rifiutare di distruggerlo a seconda di altre condizioni nel programma, come una connessione al database aperta o un file in fase di scrittura. Potresti avere un metodo "request_delete" nella classe o nel gestore che controllerà quella condizione e cancellerà o rifiuterà e restituirà uno stato che ti dice cosa ha fatto. È molto più flessibile che chiamare semplicemente "elimina".


73

Un tale oggetto non può mai essere creato in pila. Sempre sul mucchio. E la cancellazione deve essere effettuata tramite un amico o un membro. Un prodotto può utilizzare una singola gerarchia di oggetti e un gestore di memoria personalizzato - tali scenari possono utilizzare un dtor privato.

#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
Correzione: un tale oggetto può essere creato in pila (ma solo nell'ambito di un amico o di se stesso).
Thomas Eding,

Inoltre, non può contenere un oggetto statico o globale (ovvero avere "durata della memoria statica") in un'implementazione ospitata (poiché il distruttore verrebbe richiamato all'uscita dal programma).
Peter - Ripristina Monica l'


17

COM utilizza questa strategia per eliminare l'istanza. COM rende il distruttore privato e fornisce un'interfaccia per l'eliminazione dell'istanza.

Ecco un esempio di come sarebbe un metodo di rilascio.

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

Gli oggetti COM ATL sono un ottimo esempio di questo modello.


8

Aggiungendo alle risposte già presenti qui; i costruttori e i distruttori privati ​​sono piuttosto utili durante l'implementazione di una fabbrica in cui è necessario allocare gli oggetti creati sull'heap. Gli oggetti sarebbero, in generale, creati / cancellati da un membro statico o un amico. Esempio di un uso tipico:

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 classe può essere eliminata solo da sola. Utile se si sta creando qualche tentativo di oggetto contato di riferimento. Quindi solo il metodo di rilascio può eliminare l'oggetto, aiutandoti eventualmente ad evitare errori.


3

So che stavi chiedendo del distruttore privato. Ecco come utilizzo quelli protetti. L'idea è che non si desidera eliminare la classe principale tramite il puntatore alla classe che aggiunge funzionalità extra alla principale.
Nell'esempio seguente non voglio che GuiWindow venga cancellato tramite un puntatore 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 è sbagliato. Ecco un esempio di oggetto con c-tor e d-tor privati ​​creati in pila (sto usando la funzione membro statico qui, ma può essere fatto anche con la funzione amico o la classe amico).

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

Questo codice produrrà output: dentro PrivateCD :: TryMe, p._i = 8


3
Sono quasi sicuro che dirkgently significa che il codice che usa la tua classe non può creare un'istanza della classe nello stack. Ovviamente puoi sempre creare un'istanza della classe nello stack all'interno dei metodi di classe, poiché in quel contesto puoi accedere ai membri privati.
Edward Loper,

2

Potrebbe essere un modo per affrontare il problema in Windows in cui ciascun modulo può utilizzare un heap diverso, come l' heap di debug . Se quel problema non viene gestito correttamente , possono succedere cose brutte .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.