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

Weird behaviour when extracting a known interface from a polymorphic container

本文关键字:接口 多态 提取      更新时间:2023-10-16

谁能帮我理解这种行为?简而言之:

  • 我已经在一个公共容器中存储了多态对象。
  • 其中一些实现了特定的接口。我能分辨出哪些是。
  • 但是我不能使用这个接口。

下面是我总结的内容:

#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的定义,那么对象描述自己而不是被销毁。为什么方法声明的顺序很重要?

这是避免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将只是未定义的行为,老实说,你为什么看到你看到的特定行为并不重要。未定义行为是未定义的。

你要做的是:

Interface* j = dynamic_cast<Interface*>(v[1]);

这是通过运行时动态层次结构的正确转换,并将给您正确的 Interface*对应于v[1](或nullptr,如果v[1]没有此运行时类型)。一旦我们解决了这个问题,那么j->specific()打印Derived uses Interface,正如你所期望的。


这个问题很可能与虚值表对齐有关。当您执行重新解释强制转换时,由于Base没有specific,因此该特定函数的偏移量可能与~Base()对齐,因此效果是您直接调用析构函数—这就是为什么您看到了您所看到的。