Странное поведение при извлечении известного интерфейса из полиморфного контейнера
Может ли кто-нибудь помочь мне понять это поведение?
Короче:
- Я храню полиморфные объекты в общем контейнере.
- Некоторые из них реализуют определенный интерфейс. Я могу сказать, какие из них.
- Но я не могу использовать этот интерфейс.
Вот что я могу свести к следующему:
#include <iostream>
#include <vector>
// A base class
struct Base {
// A polymorphic method
virtual void describe() const {
std::cout << "Base" << std::endl;
};
virtual ~Base(){
std::cout << " Base destroyed" << std::endl;
};
};
// A specific interface
struct Interface {
virtual ~Interface(){
std::cout << " Interface Destroyed" << std::endl;
};
virtual void specific() = 0;
};
// A derived class..
struct Derived : public Base, public Interface {
virtual void describe() const {
std::cout << "Derived" << std::endl;
};
virtual void specific() {
std::cout << "Derived uses Interface" << std::endl;
};
virtual ~Derived() {
std::cout << " Derived destroyed" << std::endl;
};
};
int main() {
// Test polymorphism:
Base* b( new Base() );
Derived* d( new Derived() );
b->describe(); // "Base"
d->describe(); // "Derived"
// Ok.
// Test interface:
d->specific(); // "Derived uses Interface"
Interface* i(d);
i->specific(); // "Derived uses Interface"
// Ok.
// Here is the situation: I have a container filled with polymorphic `Base`s
std::vector<Base*> v {b, d};
// I know that this one implements the `Interface`
Interface* j((Interface*) v[1]);
j->specific(); // " Derived destroyed"
// " Interface destroyed"
// " Base destroyed"
// Why?! What did that object do to deserve this?
return EXIT_SUCCESS; // almost -_-
}
Может ли кто-нибудь сказать мне, что мне там не хватает?
Интересный факт: если я заменяю определения Base::~Base
и Base::describe
, тогда объект описывает себя, а не уничтожается. Как этот порядок имеет значение в объявлениях методов?
Ответы
Ответ 1
Это хорошая причина, чтобы избежать приведения в стиле C. Когда вы выполните:
Interface* j((Interface*) v[1]);
Это reinterpret_cast
. Сценарий в стиле C попытается сделать, чтобы: const_cast
, static_cast
, static_cast
, затем const_cast
, reinterpret_cast
, reinterpret_cast
, затем const_cast
. Все эти броски, в данном случае, ошибочны! reinterpret_cast
, в частности, будет просто быть undefined, и, честно говоря, даже не имеет значения, почему вы видите конкретное поведение, которое вы видите & dagger;. undefined поведение undefined.
Вместо этого вы хотите:
Interface* j = dynamic_cast<Interface*>(v[1]);
Это правильное выражение в динамической иерархии времени выполнения и даст вам правильный Interface*
, соответствующий v[1]
(или nullptr
, если v[1]
не имел этого типа времени выполнения). Как только мы исправим это, тогда j->specific()
печатает Derived uses Interface
, как и следовало ожидать.
& dagger; Вероятно, проблема связана с выравниванием vtable. Когда вы выполняете реинтерпрет, так как Base
не имеет specific
, возможно, что смещение этой конкретной функции выравнивается с помощью ~Base()
, поэтому эффект заключается в том, что вы непосредственно вызываете деструктор, который почему вы видите то, что видите.