C ++: объединение, содержащее экземпляры классов, вызывает неверную виртуальную функцию - PullRequest
4 голосов
/ 03 марта 2012

Я столкнулся со странным явлением при запуске следующего кода:

#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, но на выходе получилось «Knight».Я ожидал, что на выходе будет "Queen".Из моего тестирования я увидел, что когда запускается конструктор Piece :: Any по умолчанию, он вызывает конструкторы Piece :: Queen и Piece :: Knights, один за другим.Если бы я объявил Piece :: Any следующим образом:

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

(я в основном поменял местами рыцаря и королеву), то на выходе получилось бы Queen.Любая помощь будет оценена.

Спасибо

Ответы [ 2 ]

4 голосов
/ 03 марта 2012

Прежде всего - ваш конструктор, похоже, не инициализирует ни одного из своих членов. Вы должны выбрать один, например

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

Тогда согласно 9.5.4

В общем случае для изменения активного члена объединения необходимо использовать явные вызовы деструктора и размещение новых операторов

поэтому правильное переключение с коня на ферзя

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

Если вам это покажется некрасивым (как и мне), то это явный признак того, что держать объекты с нетривиальными конструкторами в объединении не очень хорошая идея (как насчет boost :: variable?).

3 голосов
/ 03 марта 2012
any.queen = Piece::Queen();

Это не значит, что вы думаете.Это эквивалентно

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

, который не может надежно работать, если any.queen не существует (потому что вы не заставили свой union содержать активного участника).

Вам нужнофактически инициализировать элемент, который вы хотите использовать, например, так:

new (&any.queen) Piece::Queen;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...