Quelle est l'utilité d'avoir un destructeur comme privé?


Réponses:


177

Fondamentalement, chaque fois que vous voulez qu'une autre classe soit responsable du cycle de vie des objets de votre classe, ou que vous avez des raisons d'empêcher la destruction d'un objet, vous pouvez rendre le destructeur privé.

Par exemple, si vous faites une sorte de comptage de références, vous pouvez demander à l'objet (ou au gestionnaire qui a été "ami") de compter le nombre de références à lui-même et de le supprimer lorsque le nombre atteint zéro. Un dtor privé empêcherait quiconque de le supprimer alors qu'il y avait encore des références.

Dans un autre cas, que se passe-t-il si vous avez un objet qui a un gestionnaire (ou lui-même) qui peut le détruire ou peut refuser de le détruire en fonction d'autres conditions du programme, telles qu'une connexion à une base de données en cours d'écriture ou un fichier en cours d'écriture. Vous pourriez avoir une méthode "request_delete" dans la classe ou le gestionnaire qui vérifiera cette condition et elle supprimera ou refusera, et retournera un statut vous indiquant ce qu'elle a fait. C'est beaucoup plus flexible que d'appeler simplement "supprimer".


73

Un tel objet ne peut jamais être créé sur la pile. Toujours sur le tas. Et la suppression doit être effectuée via un ami ou un membre. Un produit peut utiliser une seule hiérarchie d'objets et un gestionnaire de mémoire personnalisé - de tels scénarios peuvent utiliser un dtor privé.

#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
Correction: un tel objet peut être créé sur la pile (mais uniquement dans la portée d'un ami ou lui-même).
Thomas Eding

De plus, il ne peut pas être un objet statique ou global (c'est-à-dire avoir une "durée de stockage statique") dans une implémentation hébergée (car le destructeur serait appelé à la sortie du programme).
Peter - Réintègre Monica le


17

COM utilise cette stratégie pour supprimer l'instance. COM rend le destructeur privé et fournit une interface pour supprimer l'instance.

Voici un exemple de ce à quoi ressemblerait une méthode Release.

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

Les objets ATL COM sont un excellent exemple de ce modèle.


8

Ajout aux réponses déjà présentes ici; Les constructeurs et destructeurs privés sont très utiles lors de l'implémentation d'une fabrique où les objets créés doivent être alloués sur le tas. Les objets seraient, en général, créés / supprimés par un membre statique ou un ami. Exemple d'utilisation typique:

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 ne peut être supprimée que par elle-même. Utile si vous créez un essai d'objet compté de référence. Ensuite, seule la méthode release peut supprimer l'objet, vous aidant éventuellement à éviter les erreurs.


3

Je sais que vous parliez de destructeur privé. Voici comment j'utilise les protégés. L'idée est que vous ne voulez pas supprimer la classe principale via le pointeur vers la classe qui ajoute des fonctionnalités supplémentaires à la classe principale.
Dans l'exemple ci-dessous, je ne veux pas que GuiWindow soit supprimé via un pointeur 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 faux. Voici un exemple d'objet avec un c-tor privé et un d-tor créé sur la pile (j'utilise une fonction membre statique ici, mais cela peut également être fait avec une fonction ami ou une classe ami).

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

Ce code produira une sortie: à l'intérieur de PrivateCD :: TryMe, p._i = 8


3
Je suis sûr que dirkgently voulait dire que le code qui utilise votre classe ne peut pas instancier la classe sur la pile. Bien sûr, vous pouvez toujours instancier la classe sur la pile dans les méthodes de classe, car dans ce contexte, vous pouvez accéder aux membres privés.
Edward Loper

2

Cela peut être un moyen de résoudre le problème dans Windows où chaque module peut utiliser un tas différent, tel que le tas de débogage . Si ce problème n'est pas traité correctement, de mauvaises choses peuvent arriver.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.