C++: Union containing class instances calls wrong virtual function

对着背影说爱祢 提交于 2019-12-12 12:09:51

问题


I came across a strange phenomena upon running the following code:

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

The program compiled successfully on the Apple LLVM 3.0 compiler, but the output was "Knight". I was expecting for the output to be "Queen". From my testing I saw that when Piece::Any's default constructor runs, it calls both Piece::Queen and Piece::Knights' constructors, one after another. If I were to declare Piece::Any like this:

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

(I basically swapped the order of knight and queen) then the output would be Queen. Any help would be appreciated.

Thanks


回答1:


First of all - your constructor seems to initialize none of its members. You should choose one, for example

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

Then according to 9.5.4

In general, one must use explicit destructor calls and placement new operators to change the active member of a union

so correct switching from knight to queen is

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

If it looks ugly to you (as it does to me), it is clear indication, that keeping objects with non-trivial constructors in union is not a good idea (how about boost::variant?).




回答2:


any.queen = Piece::Queen();

This does not mean what you think it does. This is equivalent to

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

which cannot reliably work if any.queen does not exist (because you haven't forced your union to contain an active member).

You need to actually initialise the member you want to use, for example like this:

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


来源:https://stackoverflow.com/questions/9546914/c-union-containing-class-instances-calls-wrong-virtual-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!