12
votes

Est un pointeur sur une fonction de membre virtuel valable dans le constructeur de la classe de base?

Ma question n'est pas sur appeler une fonction de membre virtuel à partir d'un constructeur de classe de base, mais si le pointeur / em> sur une fonction de membre virtuel est valide dans le constructeur de la classe de base.

Compte tenu de la suivante xxx

va-t-il produire "en B :: vmember ()" pour tous les compilateurs C ++ conformes?


11 commentaires

Je devinerais non. Inside A's's Constructor, * Ce est un objet de classe A et la machine à livrer est une table de table. De plus, je serais surpris si & a :: vmember a abouti à un pointeur sur B :: vmember . Mais là encore, je ne suis pas un gourou C ++.


@ PÉTER Török: Oui, mais c'est le point de la table; n'est-ce pas. L'emplacement reste le même mais les sous-classes remplissent leurs pointeurs de fonction dans la table de base de la classe de base. Par conséquent, le pointeur de fonction est correct mais l'appelant dans le constructeur n'est pas. Notez que le pointeur de fonction est invoqué après la fin du constructeur


Hmmm ... pourrait être. Néanmoins, ma bonne compréhension est que les résultats & a :: vmember non dans un pointeur sur la table de table, mais un pointeur sur une fonction de membre concret.


@Peter: il compile sous g ++ 4.4.1 et des impressions "dans B :: vmember () \ n" afin que le mécanisme virtuel fonctionne dans ce cas. Mais je ne peux pas localiser une information pertinente dans la norme ...


@Francesco: c'est aussi ma compréhension. Je sais que cela compile et génère le résultat souhaité à l'aide de g ++, mais est-ce une manière documentée dans la norme?


@Dr. SBAITSO: Même sortie également avec VC ++ 2008.


@Francesco: Vous pouvez tester avec chaque compilateur possible sur Terre, si cela n'est pas clairement spécifié dans la norme, vous ne pourrez toujours pas conclure que "ça marche".


@ cerise: SPOT ON, et on ne peut même pas conclure qu'il continuera de travailler avec de nouvelles versions d'un compilateur. C'est la raison exacte pour laquelle j'ai signalé le numéro de version de g ++ et de VC ++.


Pour le point de bonus: je ne trouve nulle part dans MSDN ce qui susciterait un tel comportement. Ça me dérange complètement. b.a :: vmember () produit un résultat différent de (b. * & a :: vmember) () ... c'est certainement la surprise: /


@ cerneon: il est solidement et clairement défini dans la norme.


@Andreyt: Je suis content que ce soit :) Ce n'était pas que je ne peux pas apprendre;)


6 Réponses :


0
votes

Je pense que non. Le pointeur sur la fonction de membre virtuel est résolu via VMT, de la même manière que l'appel à cette fonction se produirait. Cela signifie qu'il n'est pas valide, car VMT est renseigné après la fin du constructeur.


14 commentaires

Notez que la question ne demande explicitement que si le pointeur est valide, ne l'appelant pas dans le constructeur. Le pointeur de fonction est invoqué après que le constructeur ait fini et que la table équipée est terminée.


Mais quel est un pointeur valide qui n'est pas valide à appeler. Soit il est valide - alors vous pouvez l'appeler, soit invalide et vous ne l'appelez peut-être pas. Quoi qu'il en soit, pourquoi un pointeur est-il nécessaire qui est invalide et ne sera pas appelé?


Il sera appelé, juste pas du constructeur. Le M_PMember est appelé à partir de la fonction TEST () dans l'exemple


@Andrey: Absolument incorrect. Aucune limitation n'a été imposée à l'invocation de la fonction virtuelle du constructeur, qu'il s'agisse d'un pointeur ou directement. La seule chose à retenir est que le type dynamique de l'objet est le type dont le constructeur fonctionne actuellement.


@Andreyt "Invocation de la fonction virtuelle du constructeur" Il y a, et je vais essayer de trouver


@Andreyt Vous êtes ici: alors juste Google "appelant des fonctions virtuelles du constructeur" et vous verrez beaucoup de raisons pour lesquelles vous ne devriez pas faire cela.


... et il n'y a pas de limitations imposées à l'initialisation du pointeur de fonction dans le constructeur, quel que soit l'endroit où ces pointeurs pointaient.


@Andrey: Vous devez avoir mal compris quelque chose. Il y a des raisons non pour lesquelles vous ne devriez pas appeler des fonctions virtuelles du constructeur. Il vous suffit de vous rappeler comment fonctionne de fonction virtuelle, lorsque vous invoquez du constructeur (ou destructeur). Tant que vous n'avez pas d'attentes irréalistes, appelez les fonctions virtuelles du constructeur conviennent parfaitement.


@Andreyt je ne serais peut-être pas juste à propos de la validité du pointeur, mais dans mon commentaire, j'ai plaidé avec la déclaration "Il n'y a pas de limitations imposées à l'invocation de la fonction virtuelle du constructeur"


@Andrey: Ce que je dis, c'est qu'il est parfaitement légal et valide pour invoquer des fonctions virtuelles du constructeur, à condition que vous sachiez ce que cela fait et tant que vous comprenez ce que vous faites.


@Andreyt Vous pensez que c'est parfaitement bien mais des experts ne Artima.com/cpppsource/NeverCall. HTML


@Andrey: Scott Meyers a fait une très bonne explication, mais a attiré une conclusion complètement fou. Je dirais que l'article que vous avez lié est destiné aux débutants, pour qui le radical "ne le fait pas !!!" Les conseils peuvent être utiles. Mais tout programmeur plus avancé C ++ sait que la conclusion Scott fait dans son article est totalement faux. Le bon conseil semble différemment: "Lorsque vous appelez la fonction virtuelle du constructeur, assurez-vous de savoir ce qui va arriver".


@Andreyt Je comprends parfaitement ce qui va se passer si j'invoque la fonction virtuelle, mais je ne pense toujours pas que c'est une bonne idée. J'aime aussi suivre les directives d'experts. Je ne pense pas que «avancé» C ++ signifie piratage et abus de langage. Il guide le code instable et mal entretenue.


@Andrey: Il n'y a absolument aucun "piratage" ou "abusant" en appelant la fonction virtuelle du constructeur. Le comportement des fonctions virtuelles (et des pointeurs à eux) dans de tels cas est strictement, clairement et sans ambiguïté défini par la norme linguistique. Quel "piratage" parlez-vous?



1
votes

lire Cet article pour une discussion approfondie des pointeurs de la fonction membre et comment les utiliser. Cela devrait répondre à toutes vos questions.


5 commentaires

meta.stackexchange.com/questions/13369/...


En outre, c'est un ancien article - par exemple, il aborde MSVC4-7. Cette question est explicitement sur compilateurs conformes .


C'est beaucoup de texte à lire lorsque ce qui a été demandé était une réponse simple oui ou non. Alors, quel est-ce?


Cela dépend beaucoup de votre graphique de héritage, mais vous obtiendrez généralement un décalage valide du début de l'objet, pas du pointeur complet.


L'article peut être vieux, mais il aborde les anciens compilateurs pour des activités de contournement. Prenez le code et voyez par vous-même. Il est amusant si non éducatif.



-1
votes

imo c'est implémentation définie pour prendre l'adresse d'une fonction virtuelle. En effet, les fonctions virtuelles sont implémentées à l'aide de VTABLES qui sont spécifiques à la mise en œuvre du compilateur. Étant donné que la tablette n'est pas garantie pour être complète tant que l'exécution de la classe CTOR est terminée, un pointeur sur une entrée dans une telle table (fonction virtuelle) peut être comportement défini de la mise en œuvre .

Il y a une question quelque peu liée à laquelle j'ai demandé si ici quelques mois en arrière; qui dit essentiellement que la prise d'adresse de la fonction virtuelle n'est pas spécifiée dans la norme C ++.

Donc, dans tous les cas même si cela fonctionne pour vous, la solution ne sera pas portable.


8 commentaires

-1, Non. Rien n'est Mise en œuvre définie Sauf si la norme nécessite explicitement des implémentations pour documenter leur comportement. Cela ne peut pas être un comportement non spécifié non plus, car il n'y a pas de gamme de comportements raisonnables.


@Msalters: O.K., pourriez-vous prendre une adresse sur les fonctions virtuelles comportement non défini?


@Abhay: pas vrai. Il n'y a pas de mise en œuvre définie sur les fonctions virtuelles, même lorsqu'elles sont invoquées par des pointeurs.


@Andreyt: S'il vous plaît clarifier "Il n'y a rien de mise en œuvre définie sur les fonctions virtuelles". Je pense que la manière dont ils sont implémentées (vtables) lui-même est définie sur la mise en œuvre.


@Abhay: Les détails des implémentations sont toujours "définis sur la mise en œuvre", aucun argument ici :) Ce que je dis, c'est que le comportement de fonctions virtuelles et de pointeurs à ceux-ci dans la langue C ++ est pas < / i> la mise en œuvre définie. Quant à la mise en œuvre de ce comportement - cela n'a pas d'importance du tout.


@Andreyt: O.K. Je conviens que mon raisonnement peut ne pas être une réponse précise à la question de l'OP sur quelle fonction sera appelée, mais la prise d'adresse des fonctions virtuelles me semble sûrement désactivée. Le poste que j'ai lié - qui traite de la prise d'adresse des fonctions virtuelles, semble suggérer que c'est la mise en œuvre définie .


@Abhay: Ehabhay: Eh bien ... en réalité le comportement dans les situations lorsque l'adresse de la fonction virtuelle est prise (et la fonction est appelée via le pointeur) n'est pas la mise en œuvre définie. Le comportement est strictement et clairement défini soit la norme.


Comment les fonctions virtuelles sont mises en œuvre dans les coulisses sont (comme d'habitude) non spécifiée - il existe une gamme de solutions possibles et une implémentation pas ne doit pas documenter lequel ils utilisent.



3
votes

"valide" est un terme spécifique lorsqu'il est appliqué aux pointeurs. Les pointeurs de données sont valides quand ils pointent sur un objet ou null ; Les pointeurs de fonction sont valides quand ils pointent sur une fonction ou null et des pointeurs des membres sont valides lorsque le point sur un membre ou null .

Cependant, de votre question À propos de la production réelle, je peux déduire que vous vouliez demander quelque chose d'autre. Regardons votre VMember Fonction - ou dois-je dire des fonctions? De toute évidence, il y a deux corps de fonction. Vous auriez pu faire uniquement le virtuel dérivé, de sorte que trop confirme qu'il y a vraiment deux fonctions vmember , qui sont tous deux virtuels.

Maintenant, la question devient si Prendre l'adresse d'une fonction membre choisit déjà la fonction réelle. Vos implémentations montrent qu'ils ne le font pas, et cela ne se produit que lorsque le pointeur est réellement déréférencé.

La raison pour laquelle il doit travail de cette façon est trivial. Prendre l'adresse d'une fonction membre n'implique pas d'objet réel, quelque chose qui serait nécessaire pour résoudre l'appel virtuel. Laissez-moi vous montrer: xxx

lorsque nous initialisons test , il n'y a aucun objet de type A ou ou ou B du tout, mais est-il possible de prendre l'adresse de & a :: vmember . Le même pointeur de membre peut ensuite être utilisé avec deux objets différents. Qu'est-ce que cela pourrait produire mais "dans un :: vmember () \ n" et "in b :: vmember () \ n"?


3 commentaires

En fait, je trouve cela dérangé que b.a :: vmember () et (b. * & A :: vmember) () Ne produisez pas le même effet ...


Devrait être (a. * Test) () . Idem pour B .


La différence déconcertante résulte d'une similitude syntaxique entre deux expressions qui ne sont pas séparativement que similaires. En particulier, le premier n'utilise pas un pointeur sur une fonction de membre virtuel. Son comportement ne traite donc pas de la question du titre.



1
votes

J'ai trouvé une petite explication sur le Nouvelle chose (un blog de Raymond Chen, parfois appelé Chuck Norris de Microsoft).

Bien sûr, il ne dit rien sur la conformité, mais cela explique pourquoi: P>

B b;

b.A::vmember(); // [1]

(b.*&A::vmember)(); // [2]


2 commentaires

Vous vouliez probablement dire que vous ne pouvez pas désactiver l'envoi de temps d'exécution lorsque vous appelez une fonction de membre via un pointeur.


Je veux dire que "Vous ne pouvez pas réellement appeler la fonction non virtuelle à l'aide d'un pointeur à la fonction membre" Cela me semble un peu trompeur.



3
votes

Le pointeur est valide, mais vous devez garder à l'esprit que lorsqu'une fonction virtuelle est appelée via un pointeur, il est toujours résolu en fonction du type dynamique de l'objet utilisé sur la gauche côté. Cela signifie que lorsque vous invoquez une fonction virtuelle du constructeur, peu importe que vous l'invoquiez directement ou si vous l'invoquez à travers un pointeur. Dans les deux cas, l'appel va résoudre au type dont le constructeur fonctionne actuellement. C'est ainsi que les fonctions virtuelles fonctionnent, lorsque vous les invoquez lors de la construction d'objets (ou de la destruction).

Notez également que les pointeurs des fonctions membres ne sont généralement pas attachés à des fonctions spécifiques au point d'initalisation. Si la fonction cible n'est pas virtuelle, elles peuvent dire que le pointeur pointe vers une fonction spécifique. Toutefois, si la fonction cible est virtuelle, il n'ya aucun moyen de dire où le pointeur pointe vers. Par exemple, la spécification linguistique indique explicitement que lorsque vous comparez (pour l'égalité) deux pointeurs qui pointent sur des fonctions virtuelles, le résultat est non spécifié . .


0 commentaires