C++ 基础 class 钻石设计中最后派生的函数调用

C++ base class function call from last derived in diamond design

我正在学习 C++,在阅读和测试了很多关于多重继承、虚拟 inheritance/methods 和菱形设计之后,直到最后我仍然有一些问题无法理解。

我在钻石设计模式中,其中 class B e C 继承 virtual public A 并且 D 继承 public B, public C:

    A
  /   \
 B     C
  \   /
    D

所有 classes 都实现了我用以下字符串初始化的私有变量 std::string _message

 "class-name instance"

只有 Class A 也实现了 virtual public display(void) const 方法。

void     display(void) const{
     std::cout << this->_message << std::endl;
}

主要是:

D foo;
foo.display();

输出为:

A instance

然而,当我 "inspect" 使用 Xcode 逐步调试元素时,我看到在 D 实例中我可以正确找到 B、C 和 A 对象(只有一个由 B 共享和 C) 正确分配了所有不同的 _message

我做错了什么?我需要覆盖 D class 中的 display() 方法吗?但如果是这样,如果我必须在派生的 class?

中重新实现相同的方法,这就是多重​​继承的真正意义所在

你的问题好像与菱形模式无关,一般是关于C++中的继承模型。 C++中的大多数东西都是静态绑定的,所以在编译时编译器会固定使用什么方法或某个名称的成员:

如果您访问隐式 this 对象的成员或使用对象的指针或引用,您将最终访问指针或引用在编译时具有的 class 的成员时间,无论在运行时是否存在具有相同名称成员的派生-class 对象。这就是为什么他们说当你在派生的class.

非虚函数也是如此。

唯一行为不同的是虚函数。这些函数可以在派生的 classes 中被覆盖,并且传递一个指向基 class 的指针的代码可以是基 class 的虚函数,并最终执行在派生 class.

所以虚函数的意义不是让编译器在派生的classes 的上下文中重新解释某个函数(正如你似乎理解的那样),而是让替换一个函数成为可能在基础 class 中由派生的 class.

中的 不同的 函数

回到你的动机:如果你想要一个 display 函数来打印一条取决于实际对象类型的消息,那么固定的东西就是 printig,它不需要虚拟的。但是你需要一个虚函数来获取对象类型。像这样:

#include <iostream>
#include <ostream>

class A {
public:
    void display() { std::cout << get_message() << '\n'; }
    virtual const char * get_message() { return "A instance"; }
};

class B : virtual public A {
public:
    virtual const char * get_message() { return "B instance"; }
};

class C : virtual public A {
public:
    virtual const char * get_message() { return "C instance"; }
};

class D : public B, public C {
public:
    // virtual const char * get_message() { return B::get_message(); }
    // virtual const char * get_message() { return C::get_message(); }
    // virtual const char * get_message() { return "D instance"; }
};

int main(void)
{
    D foo;
    foo.display();
    A* a_ptr = &foo;
    a_ptr->display();
    return 0;
}

给出的示例将无法编译(现在这是由于菱形模式),因为编译器无法决定 A::get_message() 的覆盖程序是 B::get_message() 还是 C::get_message() 应该在D对象中pick,你需要在D真实代码中做一个注释来为D声明一个唯一的get_message,其中可以复用已有的函数。