Pourquoi quelqu'un déclarerait-il un constructeur protégé? Je sais que les constructeurs sont déclarés privés dans le but de ne pas autoriser leur création sur pile.
Lorsqu'une classe est (conçue comme) une classe abstraite, un constructeur protégé a parfaitement raison. Dans cette situation, vous ne voulez pas que les objets soient instanciés de la classe, mais vous ne les utilisez que pour en hériter.
Il existe d'autres cas d'utilisation, comme lorsqu'un certain ensemble de paramètres de construction doit être limité aux classes dérivées.
Les constructeurs non publics sont utiles lorsqu'il existe des exigences de construction qui ne peuvent être garanties uniquement par le constructeur. Par exemple, si une méthode d'initialisation doit être appelée juste après le constructeur, ou si l'objet doit s'enregistrer auprès d'un objet conteneur/gestionnaire, cela doit être fait en dehors du constructeur. En limitant l'accès au constructeur et en ne fournissant qu'une méthode d'usine, vous pouvez vous assurer que toute instance qu'un utilisateur reçoit remplira toutes ses garanties. Ceci est également couramment utilisé pour implémenter un Singleton, ce qui est vraiment juste une autre garantie de la classe (qu'il n'y aura qu'une seule instance).
La raison pour laquelle le constructeur est protégé, plutôt que privé, est la même que pour rendre toute autre méthode ou champ protégé au lieu de privé: afin qu'il puisse être hérité par les enfants. Vous souhaitez peut-être une méthode de fabrique publique non virtuelle dans la classe de base, qui renvoie des références aux instances des classes dérivées; les classes dérivées veulent évidemment avoir accès aux constructeurs parents, mais vous ne voulez toujours pas les créer en dehors de votre usine.
une utilisation pourrait être des modèles d'usine
Un constructeur protégé peut être utilisé pour rendre une classe effectivement abstraite lorsqu'aucune de ses méthodes n'est pure-virtuelle.
Ce n'est pas tout à fait abstrait au sens C++ car les classes amis peuvent toujours l'utiliser sans redéfinir, mais alors vous devrez les déclarer.
Pour les méthodes d'usine avec effets secondaires.
class mine {
private:
mine () {};
protected:
mine(int id) : m_id(id) {};
int m_id;
static int m_count;
public:
static mine* CreateOneOfMe() {
return mine(m_count++);
}
int GetId() { return m_id; }
};
Cela crée des instances de la classe et garantit que chacune d'elles a un identifiant entier incrémenté unique. Notez que si le constructeur que vous souhaitez utiliser n'est pas celui par défaut, vous devez également masquer celui par défaut.
Un constructeur protégé signifie que seuls les membres dérivés peuvent construire des instances de la classe (et des instances dérivées) à l'aide de ce constructeur. Cela semble un peu poulet et œuf, mais est parfois utile lors de la mise en œuvre des usines de classe.
Pour permettre à une sous-classe d'utiliser un constructeur qui ne devrait pas être directement accessible à un instanciateur.
Vous pouvez l'utiliser pour limiter les classes qui pourraient le créer, par exemple:
class Level
{
private:
Level();
¨Level();
friend class LevelManager;
};
La seule classe qui peut en créer une instance est la classe LevelManager, vous saurez donc toujours que l'instance Level est créée dans le LevelManager.
Une utilisation du constructeur protégé est d'implémenter le modèle CRTP, voir le code ci-dessous:
#include <iostream>
#include <assert.h>
template <class T>
class ComparableMixin {
public:
bool operator !=(ComparableMixin &other) {
return ~(*static_cast<T*>(this) == static_cast<T&>(other));
}
bool operator <(ComparableMixin &other) {
return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
}
bool operator >(ComparableMixin &other) {
return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
}
bool operator >=(ComparableMixin &other) {
return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
}
protected:
ComparableMixin() {}
};
class Integer: public ComparableMixin<Integer> {
public:
Integer(int i) {
this->i = i;
}
int i;
bool operator <=(Integer &other) {
return (this->i <= other.i);
}
bool operator ==(Integer &other) {
return (this->i == other.i);
}
};
int main() {
Integer i(0) ;
Integer j(1) ;
//ComparableMixin<Integer> c; //compilation error!
assert (i < j );
assert (i != j);
assert (j > i);
assert (j >= i);
return 0;
}