【问题标题】:Call an interface function from an unknown derived class (multiple inheritance)从未知的派生类调用接口函数(多重继承)
【发布时间】:2026-02-13 23:00:01
【问题描述】:

我有一个 Base* 对象数组。这包含一堆派生对象,其中一些可能实现Interface

struct Base {
    virtual void doNotCallThis() { cout << "nooo" << endl; }
};

struct Interface {
    virtual void doThis() = 0;
};

// Example derived class
struct Derived : Base, virtual Interface {
    virtual void doThis() { cout << "yes" << endl; }
};

int main() {
    Base* b[1];
    b[0] = new Derived(); // Here would be a bunch of different derived classes
    ((Interface*)b[0])->doThis(); // Elsewhere, doThis() would be called for select array elements
    return 0;
}

输出:

nooo

我不知道运行时b[i] 的确切类型,所以我无法转换为Derived(可能是Derived2Derived3 等)。如果这是一个解决方案,我也不能使用 dynamic_cast。我所知道的是,当我调用doThis() 时,b[i] 是一个继承自Interface 的类型。我试图在上面调用它的方式会导致调用错误的函数,例如。 Base::doNotCallThis().

如何正确称呼它?

【问题讨论】:

  • 您不能合法地将b[0] 转换为Interface*,这只是未定义的行为。
  • 我本以为正确的做法是引入一个继承BaseInterface 的中间基类,所有Derived 类都可以依赖-然后使@987654338 @进入那个班级。
  • @MatsPetersson 我会这样做,但一些 Derived 类继承自 Base 的派生,这将导致它们派生两次。
  • 因此必须重新定义这些类才能从新的中间类继承!
  • @MatsPetersson 如果这就是你所说的,有点困惑:我有BaseBase1 : BaseBase2 : Base 等等。说 10 个推导。因此,对于与Interface 一起使用的每一个,我都必须定义JoinedBaseNumberX : BaseX, virtual Interface?然后我也不知道要使用哪个连接派生......

标签: c++ function inheritance polymorphism


【解决方案1】:

正如其他人所指出的,您可能最好找到一种方法来重构您的设计,这样就不需要强制转换了。

但抛开这些,我可以解释发生了什么问题以及如何正确施放。

((Interface*)b[0]) 的问题在于,由于 BaseInterface 不相关,编译器必须进行盲目的重新解释转换。实际上,这意味着在这种情况下,结果指针实际上并不与对象的 Interface 部分对齐。如果您尝试static_cast&lt;Interface*&gt;(b[0]),您会发现它无法编译 - 这是一个很大的暗示,表明这是一种错误的演员阵容。

另一方面,编译器确实知道从BaseDerived 以及从DerivedInterface 的关系。因此,只要您确定知道该对象不仅实现了Interface,而且还是一个Derived,那么您就可以这样做:

static_cast<Interface*>(static_cast<Derived*>(b[0]))->doThis();

但是,如果您的设计有多个独立实现 Interface 的不同派生类型,那么您可能无法做到这一点,除非您再次绝对随时知道派生类型是什么拨打电话。 - 这就是为什么将它重构为更好的类层次结构更可取的原因,因为它不那么脆弱和繁琐。

(作为旁注,这个问题指出了为什么在向上和向下移动类层次结构时永远不要使用原始/重新解释转换是一个好主意。至少使用static_cast,因为 can 编译器可以更好地帮助你正确地做到这一点.)

【讨论】:

  • 很好的解释,我试图完成的愚蠢现在很有意义。我最终重构了我的代码,将Interface 中的代码直接放入Base--说接口应该只应用于Base-派生对象。
【解决方案2】:

写一个有被否决的风险的答案:

如果我们从::开始

struct Base()
{
   virtual void SomeFunc();
};

struct Interface
{
   virtual void doThis();
}

然后要从 Base 创建一堆也是接口的派生函数,我会做这样的事情:

struct BaseInterface : public Base, public Interface
{
  // Nothing here - this is just combining Base and Interface 
};

struct Base1 : public BaseInterface
{
   ... add stuff that Base1 has that isn't in Base.
};

struct Derived: public Base1
{
  ... some more stuff that isn't in Base1 
}

然后我们像这样在 Main 中使用它:

int main() {
    BaseInterface* b[1];
    b[0] = new Derived(); // Here would be a bunch of different derived classes
    b[0])->doThis(); // Elsewhere, doThis() would be called for select array elements
    return 0;
}

【讨论】:

  • 并没有完全使用它,但它确实帮助我意识到界面本身存在一个基本的设计问题;我最终只是将它直接合并到 Base 中(而不是从它们都派生到一个新的基础中)。不知道如果我有更多接口,或者至少是那些不太兼容的接口,这会有多好,但在这种情况下,你的回答解决了我的问题,谢谢。