C ++: La classe contenant des instances de classe appelle une fonction virtuelle incorrecte

J’ai rencontré un phénomène étrange en exécutant le code suivant:

#include  class Piece { public: class Queen; class Knight; union Any; virtual const char* name() const = 0; }; class Piece::Queen : public Piece { public: virtual const char* name() const { return "Queen"; } }; class Piece::Knight : public Piece { public: virtual const char* name() const { return "Knight"; } }; union Piece::Any { public: Any() {} Piece::Queen queen; Piece::Knight knight; }; using namespace std; int main(int argc, const char* argv[]) { Piece::Any any; any.queen = Piece::Queen(); cout << any.queen.name() << endl; return 0; } 

Le programme a été compilé avec succès sur le compilateur Apple LLVM 3.0, mais le résultat était “Knight”. Je m’attendais à ce que la sortie soit “Queen”. D’après mes tests, j’ai constaté que lorsque le constructeur par défaut de Piece :: Any s’exécute, il appelle les deux constructeurs Piece :: Queen et Piece :: Knights l’un après l’autre. Si je devais déclarer Piece :: N’importe lequel comme ceci:

 union Piece::Any { public: Any() {} Piece::Knight knight; Piece::Queen queen; }; 

(J’ai fondamentalement échangé l’ordre des chevaliers et des reines) alors la sortie serait la reine. Toute aide serait appréciée.

Merci

Tout d’abord, votre constructeur ne semble initialiser aucun de ses membres. Vous devriez en choisir un, par exemple

 Piece::Any::Any(): knight() {} 

Puis selon 9.5.4

En général, il faut utiliser des appels de destructeurs explicites et placer de nouveaux opérateurs pour changer le membre actif d’une union.

si bon passage de chevalier à la reine est

 any.knight.~Knight(); new(&any.queen) Queen; 

Si cela vous semble laid (comme cela m’est utile), cela indique clairement que garder des objects avec des constructeurs non sortingviaux en union n’est pas une bonne idée (que diriez-vous de boost :: variante?).

 any.queen = Piece::Queen(); 

Cela ne signifie pas ce que vous pensez qu’il fait. Ceci est équivalent à

 any.queen.operator=(Piece::Queen()); 

qui ne peut pas fonctionner de manière fiable si any.queen n’existe pas (car vous n’avez pas forcé votre union à contenir un membre actif).

Vous devez réellement initialiser le membre que vous voulez utiliser, par exemple comme ceci:

 new (&any.queen) Piece::Queen;