C ++:包含类实例的联合调用错误的虚函数

时间:2012-03-03 14:35:14

标签: c++ class virtual call unions

我在运行以下代码时遇到了一个奇怪的现象:

#include <iostream>    

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;
}

程序在Apple LLVM 3.0编译器上成功编译,但输出是&#34; Knight&#34;。 我期待输出是&#34; Queen&#34;。 从我的测试中我看到,当Piece :: Any的默认构造函数运行时,它会调用Piece :: Queen和Piece :: Knights&#39;建设者,一个接一个。如果我要声明片段::任何像这样:

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

(我基本上交换了骑士和女王的顺序)然后输出将是女王。 任何帮助将不胜感激。

由于

2 个答案:

答案 0 :(得分:4)

首先 - 你的构造函数似乎没有初始化它的成员。你应该选择一个,例如

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

然后根据9.5.4

  

通常,必须使用显式析构函数调用和放置新运算符来更改联合的活动成员

所以从骑士到女王的正确切换是

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

如果它对你来说看起来很难看(就像它对我一样),很清楚地表明,保持带有非平凡构造函数的对象并不是一个好主意(boost :: variant怎么样?)。

答案 1 :(得分:3)

any.queen = Piece::Queen();

这并不意味着你的想法。这相当于

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

如果any.queen不存在,则无法可靠地工作(因为您没有强制union包含活动成员)。

您需要实际初始化您要使用的成员,例如:

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