Prévenir l'héritage de classe en C++
Récemment, un de mes ami m'a demandé comment faire pour empêcher l'héritage de classe en C++. Il voulait la compilation échoue.
Je pensais à elle et a trouvé 3 réponses. Vous ne savez pas lequel est le meilleur.
1) Constructeur(s)
class CBase
{
public:
static CBase* CreateInstance()
{
CBase* b1 = new CBase();
return b1;
}
private:
CBase() { }
CBase(CBase3) { }
CBase& operator=(CBase&) { }
};
2) à l'Aide de CSealed de la classe de base, privé ctor & héritage virtuel
class CSealed
{
private:
CSealed() {
}
friend class CBase;
};
class CBase : virtual CSealed
{
public:
CBase() {
}
};
3) à l'Aide d'un CSealed de la classe de base, protégé ctor & héritage virtuel
class CSealed
{
protected:
CSealed() {
}
};
class CBase : virtual CSealed
{
public:
CBase() {
}
};
Toutes les méthodes ci-dessus, assurez-vous que CBase classe ne peut pas être héritée plus loin.
Ma Question est:
1) Qui est la meilleure méthode ? Toutes les autres méthodes disponibles ?
2) Méthode 2 & 3 ne fonctionnera pas à moins que le CSealed classe est héritée virutally. Pourquoi est-ce ? A-t-elle quelque chose à voir avec vdisp ptr ??
PS:
Le programme ci-dessus a été compilé dans MS compilateur C++ (Visual Studio).
référence : http://www.codeguru.com/forum/archive/index.php/t-321146.html
Vous devez vous connecter pour publier un commentaire.
Comme du C++11, vous pouvez ajouter le mot-clé final de votre classe, par exemple,
La principale raison que je vois pour vouloir le faire (et la raison pour laquelle je suis à la recherche pour cette question) est à marquer d'une classe non subclassable de sorte que vous pouvez utiliser en toute sécurité un non-destructeur virtuel et d'éviter une vtable tout à fait.
Vous ne pouvez pas empêcher l'héritage (avant C++11 est
final
mot-clé) - vous ne pouvez vous empêcher l'instanciation de classes héritées. En d'autres termes, il n'y a aucun moyen de prévenir:Le meilleur que vous pouvez faire est d'empêcher les objets de type B d'être instanciée. Cela étant le cas, je vous suggère de prendre kts, les conseils et le document le fait qu'Un (ou autre) n'est pas destiné à être utilisé pour l'héritage, de lui donner un non-destructeur virtuel, et pas d'autres fonctions virtuelles, et d'en rester là.
Vous allez par le biais de contorsions pour prévenir d'autres sous-classement. Pourquoi? Document le fait que la classe n'est pas extensible et faire la dtor non-virtuel. Dans l'esprit de c, si quelqu'un veut vraiment ignorer la façon dont vous l'intention de ce à être utilisé pourquoi arrêter? (Je n'ai jamais vu le point de
final
classes/méthodes en java soit).@ReallyOverride?
final
mot a un sens, car toutes les fonctions sont virtuel par défaut, et il y a donc beaucoup à gagner en permettant au compilateur JIT pour réaliser ces optimisations. En C++, il n'y aurait rien à gagner d'un mécanisme similaire pour éviter de sous-classement, c'est pourquoi la langue n'a pas de fournir une mecehanism pour le faire.final
dans ce but. Donc je m'attends à ce que ce soit le idiomatiques façon de le faire en C++11. Pourquoi utiliser les commentaires quand vous avez une fonctionnalité du langage pour faire exactement ce que les commentaires d'essayer (mais ne parviennent pas) pour faire respecter?final
identificateur, est la meilleure option. Il permet au compilateur de vous aider, et des documents très justement que cette classe n'est pas prévu pour l'héritage. Si vous choisissez de prendre le chemin de commentaires, alors vous avez le programmeur de regarder jusqu'à la source, et d'interpréter le commentaire. Qui peut être sur plusieurs lignes, peut-être même au sein d'un gros bloc de commentaire. En utilisantfinal
l'IDE peut aider à leur première tentative pour hériter de la classe. Outre l'utilisation definal
le développeur sait où regarder, même si il/elle n'est pas utilisée pour le style de programmation utilisé.std::unary_function
par exemple). stackoverflow.com/questions/7403883/...final
sur mon struct est parce que je l'utilise, en valeur, dans unstd::vector
. Si quelqu'un s'étend et devient l'un dans l'std::vector
, c'est vraiment mal.1) est une question de goût. Si je vois bien, votre plus de fantaisie 2e et 3e solutions de déplacer l'erreur, dans certaines circonstances, de lien fois au moment de la compilation, qui, en général, devrait être mieux.
2) l'héritage Virtuel est nécessaire pour forcer la responsabilité pour initialiser le (virtuel) de la classe de base pour la plupart de la classe dérivée d'où la classe de base ctor n'est plus accessible.
Pour répondre à votre question, vous ne pouvez pas hériter de CBase parce que dans l'héritage virtuel une classe dérivée aurait besoin d'avoir un accès direct à la classe à partir de laquelle il a été hérité pratiquement. Dans ce cas, une classe qui découleraient de CBase aurait besoin d'avoir un accès direct à CSealed qui il ne peut pas puisque le constructeur est privé.
Bien que je ne vois pas l'utilité de tout cela (c'est à dire: arrêt de l'héritage) vous pouvez généraliser l'utilisation des modèles (je ne pense pas qu'il compile sur tous les compilateurs, mais il le fait avec MSVC)
Si vous le pouvez, j'irais pour la première option (constructeur privé). La raison en est que à peu près tout connu programmeur C++ verrez que d'un coup d'oeil et être en mesure de reconnaître que vous essayez d'éviter de sous-classement.
Il y a peut être d'autres plus délicat des méthodes pour prévenir la sous-classement, mais dans ce cas, le plus simple sera le mieux.
Je l'ai trouvé ici pour donner du crédit. Je suis de poster ici juste d'autres personnes peuvent facilement accéder à
http://www.devx.com/tips/Tip/38482
D'élaborer sur La réponse de françois: si la classe
Bottom
dérive de la classeMiddle
, qui a pratiquement hérite de la classeTop
, c'est que la plupart de la classe dérivée (Bottom
) qui est responsable de la construction de la quasi hérité de la classe de base (Top
). Sinon, dans le multi-héritage/diamond-de-mort scénario (où l'héritage virtuel est classiquement utilisé), le compilateur ne sais pas lequel des deux "middle classes" doit construire une seule classe de base. LeMiddle
's du constructeur appel à laTop
s'constructeur est donc ignorée lorsqueMiddle
est construit à partir deBottom
:Ainsi, dans l'approche 2) ou 3) dans votre question,
Bottom()
ne peux pas appelerTop()
parce que c'est héréditaire en privé (par défaut, comme dans le code, mais il vaut la peine de rendre explicite) dansMiddle
et n'est donc pas visible dansBottom
. (source)La solution: