2010-01-28 3 views
2

Учитывая следующий кодНаследовать от двух полиморфных классов

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

reinterpret_cast - корень всего зла. Он был создан только для того, чтобы лидер вашей команды знал, что нужно для поиска в вашем коде, чтобы найти ваши ошибки;). –

+0

@KornelKisielewicz, Это потрясающая цитата! –

+0

вы можете вызывать 'foo' на любом указателе на' T' или 'A'. 'foo' не имеет ничего общего с' U'. Поэтому я не совсем понимаю ваш комментарий о «виртуальных объявлениях» в 'U'. –

ответ

4

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

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

Прямое приведение от одного базового указателя к другому (что вы пытаетесь сделать в своем коде) называется cross-cast. Единственный листинг на 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 
+0

отражают полиморфный комментарий. – ezpz

1

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

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

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

+0

Спасибо. Я не знал о 'virtual public' – ezpz

+0

@ezpz - разница, когда вы наследуете фактически, вы наследуете один и тот же базовый класс, поэтому формируете' <> ', а не' \/'при наличии общего базового класса –

0

t Populate так же, как вы делали u:

t.push_back(a); 

вы не необходимо reinterpret_cast, потому что A является T.

+0

Думаю, он явно этого не хотел. –

+0

В моем конкретном случае у меня нет доступа к исходному объекту, вложенному в вектор, у меня есть только объект в самом векторе. Именно по этой причине я пытаюсь это сделать. – ezpz

+0

@ezpz, используйте 'dynamic_cast', тогда помните, что он может вернуть NULL. –

 Смежные вопросы

  • Нет связанных вопросов^_^