L'appel de fonction virtuelle de destructeur
Est-il sécuritaire ?
class Derived: public PublicBase, private PrivateBase
{
...
~Derived()
{
FunctionCall();
}
virtual void FunctionCall()
{
PrivateBase::FunctionCall();
}
}
class PublicBase
{
virtual ~PublicBase(){};
virtual void FunctionCall() = 0;
}
class PrivateBase
{
virtual ~PrivateBase(){};
virtual void FunctionCall()
{
....
}
}
PublicBase* ptrBase = new Derived();
delete ptrBase;
Ce code crases parfois avec la propriété intellectuelle dans une mauvaise adresse.
Qui n'est pas une bonne idée d'appeler une fonction virtuelle sur le constructeur est clair pour tout le monde.
Des articles comme http://www.artima.com/cppsource/nevercall.html je comprends que le destructeur est aussi un bon endroit pour appeler une fonction virtuelle.
Ma question est "Est-ce vrai ?" J'ai testé avec VS2010 et VS2005 et PrivateBase::FunctionCall est appelé. Est un comportement indéfini ?
- Vous obtenez un comportement indéterminé de la suppression d'un pointeur vers la base où le destructeur n'est pas marquée
virtual
. Aussi, vous devriez avoir une ambiguïté entre les deux bases, puisque les deux fonctions ont la même signature - qui êtes-vous impérieuses? OIE, postez votre code réel, celui-ci n'a même pas compiler. - Désolé pour la confusion: le code réel est trop complexe, Dérivé n'est pas un destructeur virtuel, Les classes de Base n'ont.
- Une fois qu'une classe est un destructeur virtuel, tous les objets qui héritent de il virtuel destructeurs, si le code de la montre ou pas. Personnellement, je préfère tapant l'
virtual
mot-clé, mais il est entièrement facultatif (il en va de même pour toute autre fonction virtuelle: les remplacements de la fonction virtuelle sera virtuels, qui se sont déclarées comme telles ou non dans le code). - Essayez primordial
FunctionCall
dans unTooDerived : pulic Derived
classe. Qui ne sera pas appelé. - -1 Ce code ne compile pas en raison du manque de points-virgules. Ce n'est pas important en soi, mais cela signifie que c'est le code. Aussi, l'exemple est incomplètes, et, sur cette base, je vais voter pour fermer.
- Qui semble exagérée. "Le code réel" n'est pas l'objectif sous-jacent; l'objectif est "de code qui sont sans ambiguïté illustre bien le coeur du problème" - dont ce code n'a clairement.
- une bonne référence: wiki.sei.cmu.edu/confluence/display/cplusplus/...
Vous devez vous connecter pour publier un commentaire.
Je vais aller à contre-courant ici... mais d'abord, je dois supposer que votre
PublicBase
destructeur est virtuel, car sinon, leDerived
destructeur ne sera jamais appelé.Il n'est généralement pas une bonne idée d'appeler une fonction virtuelle à partir d'un constructeur/destructeur
La raison pour cela est que la dynamique de l'expédition est étrange au cours de ces deux opérations. Le type réel de l'objet changements cours de construction et il changements de nouveau lors de la destruction. Quand un destructeur est en cours d'exécution, l'objet est exactement de ce type, et jamais un type qui en découlent. Répartition dynamique est en effet à tous les temps, mais le final overrider de la fonction virtuelle va changer en fonction de l'endroit dans la hiérarchie sont.
Qui est, vous ne devriez jamais s'attendre à un appel à une fonction virtuelle dans un constructeur/destructeur pour être exécuté dans n'importe quel type dérivé du type du constructeur/destructeur en cours d'exécution.
Mais
Dans votre cas particulier, la final overrider (au moins pour cette partie de la hiérarchie) est ci-dessus votre niveau. En outre, vous êtes pas à l'aide de dynamic dispatch à tous. L'appel
PrivateBase::FunctionCall();
est résolu statiquement, et en fait l'équivalent d'un appel à une fonction non virtuelle. Le fait que la fonction est virtuel ou non n'a pas d'incidence sur cet appel.Donc oui c'est bien de faire comme vous le faites, même si vous serez obligé de l'expliquer dans des revues de code, comme la plupart des gens apprennent le mantra de la règle plutôt que la raison.
Derived::~Derived()
(parce que nous avons destructeur virtuel) qui va appelerFunctionCall
(lors de la destruction est de ce type: PublicBase); il faudra donc se résoudre àPublicBase::FunctionCall()
qui est virtuelle pure. C'est que corect ?~Derived
, le type de l'objet estDerived
(et, par extension, aussiPublicBase
etPrivateBase
) C'est quel que soit le type statique de l'pointeur à travers lequel vous supprimez (acquis que le destructeur de ce type est virtuel, comme c'est le cas). Lors de l'exécution de~Derived
, l'appel àPrivateBase::FunctionCall()
est pas répartition dynamique, c'est un appel direct à la fonction (en raison de la qualificationPrivateBase::
)FunctionCall
dansDerived::~Derived()
est résolu àDerived::FunctionCall
ouPublicBase::FunctionCall
(l'objet est exactement de ce type) ? Je comprends quePrivateBase::FunctionCall()
n'est pas une répartition dynamique en raison de la qualification, mais cet appel est à l'étape suivante.delete
est appelé,~Derived
sera appelée par le biais de la distribution dynamique, comme~PublicBase
est virtuel. À ce stade, l'objet est de typeDerived
et c'est à ce moment que le destructeur corps est exécutée etPrivateBase::FunctionCall
est appelé.this
.Oui. L'appel d'une fonction virtuelle à partir d'un constructeur ou un destructeur, les dépêches de la fonction comme si l'objet est de type dynamique ont été actuellement en cours de construction ou de destruction. Dans ce cas, il est appelé à partir du destructeur de
Derived
, il est donc envoyé àDerived::FunctionCall
(ce qui, dans votre cas, les appelsPrivateBase::FunctionCall
non virtuellement). Tout cela est bien définie.C'est "pas une bonne idée" pour appeler des fonctions virtuelles à partir d'un constructeur ou un destructeur pour trois raisons:
En général, il n'est pas une bonne idée d'appeler une fonction virtuelle, sauf si l'objet de la classe il peut être distribué (par exemple, le "plein" de l'objet de la plupart de la classe dérivée) est entièrement construit. Et ce n'est pas le cas
C'est une très mauvaise idée selon scott: lien
C'est ce que j'ai compilé et exécuté pour m'aider à acquérir une meilleure compréhension du processus de destruction, vous pouvez également trouver utile
~Derived
a été exécuté avant l'appel à~Base
.~Derived
s'exécute en premier, puis les membres de données deDerived
sont détruits, alors~Base
est exécutée.Base
appelsDerived.Somthing()
ensuite, nous avons de gros problèmes depuisDerived
a déjà appelédelete important_pointer
sûrement~Base()
appelle une fonction virtuelle à l'appel va à la version définie pour~Base()
. Il n'appelle pas de versions dérivées.