Наследовать от двух полиморфных классов - PullRequest
2 голосов
/ 29 января 2010

С учетом следующего кода

class T {
    public:
        virtual ~T () {}
        virtual void foo () = 0;
};

class U {
    public:
        U() {}
        ~U() {}
        void bar () { std::cout << "bar" << std::endl; }
};

class A : public U, public T {
    public:
        void foo () { std::cout << "foo" << std::endl; }
};

int main () {
    A * a = new A;

    std::vector<U*> u;
    std::vector<T*> t;

    u.push_back(a);

    t.push_back(reinterpret_cast<T*>(u[0]));

    u[0]->bar ();
    t[0]->foo ();

    delete a;
    return 0;
}

Я получаю вывод, который ожидал

bar
foo

Однако, если я изменю определение U на

class U {
    public:
        U() {}
        virtual ~U() {}
        virtual void bar () { std::cout << "bar" << std::endl; }
};

Я все еще компилирую нормально и без предупреждений / ошибок, но вывод теперь

bar
bar

Что это за виртуальная декларация, которая мешает мне звонить в foo?

Ответы [ 3 ]

4 голосов
/ 29 января 2010

Во-первых, в вашем примере нет виртуальных базовых классов . Классы, которые содержат виртуальные функции, называются polymorphic . (В C ++ существует такая вещь, как «виртуальные базовые классы», но она не имеет ничего общего с вашим примером.)

Во-вторых, поведение вашего кода не зависит от каких-либо виртуальных объявлений. Вы сознательно уничтожили целостность базового указателя с помощью reinterpret_cast. По этой причине поведение кода undefined .

Прямое приведение от одного базового указателя к другому (что вы пытаетесь сделать в своем коде) называется перекрестное приведение . Единственное приведение в C ++, которое может выполнять перекрестное приведение, это dynamic_cast.

t.push_back(dynamic_cast<T *>(u[0])); 

Вы можете выполнить косвенное перекрестное приведение без dynamic_cast, но для этого сначала нужно уменьшить указатель на производный тип (A *), используя static_cast, а затем преобразовать его в другой базовый тип указателя

t.push_back(static_cast<A *>(u[0])); // upconversion to `T *` is implicit
1 голос
/ 29 января 2010

Если вы используете reinterpret_cast, вы теряете все гарантии, и все, что вы делаете, это «неопределенное поведение». В этом случае я ожидаю, что VMT испортился или VPTR перезаписан.

В качестве иллюстрации, когда я компилирую первый приведенный выше код, я получаю ошибку при выполнении на моем компиляторе.

Если вы действительно хотите «выполнить перекрестное выполнение», вы должны наследовать общий базовый класс и наследовать этот базовый класс по U и T практически (: virtual public) или использовать вместо него dynamic_cast reinterpret_cast.

0 голосов
/ 29 января 2010

Заполните t точно так же, как вы u:

t.push_back(a);

Вам не нужно reinterpret_cast, потому что A - это T.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...