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

iag*_*ito 12 c++ polymorphism interface 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 -_-
}
Run Code Online (Sandbox Code Playgroud)

谁能告诉我我在那里失踪了什么?

有趣的事实:如果我交换了Base::~Base和的定义Base::describe,那么对象描述自己而不是被销毁.为什么命令在方法声明中很重要?

Bar*_*rry 21

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

Interface* j((Interface*) v[1]);
Run Code Online (Sandbox Code Playgroud)

那是一个reinterpret_cast.A C风格的类型转换会尽量做到,依次是:const_cast,static_cast,static_cast然后const_cast,reinterpret_cast,reinterpret_cast然后const_cast.在这种情况下,所有这些演员都是错的!reinterpret_cast特别是只是未定义的行为,老实说甚至不重要为什么你看到你看到的具体行为.未定义的行为未定义.

你想要做的是:

Interface* j = dynamic_cast<Interface*>(v[1]);
Run Code Online (Sandbox Code Playgroud)

这是通过运行时动态层次结构的正确转换 ,并将为您提供正确的Interface*对应v[1](或者nullptr,如果v[1]没有此运行时类型).一旦我们修复了它,然后j->specific()打印Derived uses Interface,就像你期望的那样.


可能,这个问题与vtable对齐有关.当您重新解释强制转换时,由于Base没有a specific,因此该特定函数的偏移量可能与之对齐~Base(),因此效果是您直接调用析构函数 - 这就是您看到所看到的内容的原因.