有人能帮我理解这种行为吗? 简而言之:
以下是我的意见:
#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
的定义,那么该对象描述自己而不是被销毁。为什么命令在方法声明中很重要?
答案 0 :(得分:21)
这是避免C风格演员阵容的一个很好的理由。当你这样做时:
Interface* j((Interface*) v[1]);
那是reinterpret_cast
。 C样式的演员会按顺序尝试:const_cast
,static_cast
,static_cast
然后const_cast
,reinterpret_cast
,reinterpret_cast
然后{ {1}}。在这种情况下,所有这些演员都是错的! const_cast
尤其只是未定义的行为,而且老实说甚至不重要为什么你会看到你看到的特定行为†。未定义的行为未定义。
您想要做的是:
reinterpret_cast
这是通过运行时动态层次结构的正确转换,并将为您提供与Interface* j = dynamic_cast<Interface*>(v[1]);
(或Interface*
相对应的正确 v[1]
,如果{{ 1}}没有这个运行时类型)。解决之后,nullptr
会打印v[1]
,如您所料。
<小时/> †可能这个问题与vtable对齐有关。当您重新解释演员表时,由于
j->specific()
没有Derived uses Interface
,因此该特定函数的偏移可能与Base
对齐,因此效果是您直接调用析构函数 - 这就是为什么你看到你所看到的。