Réponses:
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".
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;
}
Lorsque vous ne voulez pas que les utilisateurs accèdent au destructeur, c'est-à-dire que vous voulez que l'objet ne soit détruit que par d'autres moyens.
http://blogs.msdn.com/larryosterman/archive/2005/07/01/434684.aspx donne un exemple, où l'objet est compté par référence et ne doit être détruit par l'objet lui-même que lorsque le nombre passe à zéro.
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.
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
}
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.
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(){}
};
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