从多态容器中提取已知接口时的奇怪行为

时间:2015-08-18 15:40:56

标签: c++ interface polymorphism multiple-inheritance

有人能帮我理解这种行为吗? 简而言之:

  • 我将多态对象存储在一个公共容器中。
  • 其中一些实现了特定的接口。我可以分辨出哪些。
  • 但我无法使用此界面。

以下是我的意见:

#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::~BaseBase::describe的定义,那么该对象描述自己而不是被销毁。为什么命令在方法声明中很重要?

1 个答案:

答案 0 :(得分:21)

这是避免C风格演员阵容的一个很好的理由。当你这样做时:

Interface* j((Interface*) v[1]);

那是reinterpret_cast。 C样式的演员会按顺序尝试:const_caststatic_caststatic_cast然后const_castreinterpret_castreinterpret_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对齐,因此效果是您直接调用析构函数 - 这就是为什么你看到你所看到的。