C ++基类函数调用从最后派生出来的钻石设计

时间:2015-02-21 00:19:45

标签: c++ inheritance virtual diamond-problem

我正在学习C ++,在阅读并测试了很多关于多重继承,虚拟继承/方法和钻石设计之后,我仍然有一些问题要理解它直到最后。

我在钻石设计模式中,B类e C继承virtual public A而D继承public B, public C

    A
  /   \
 B     C
  \   /
    D

所有类都实现了一个私有变量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

然而,当我&#34;检查&#34;使用Xcode逐步调试元素我看到在D实例中我可以正确地找到B,C和A对象(只有一个由B和C共享),并且正确分配了所有不同的_message

我做错了什么?我需要覆盖D类中的display()方法吗?但如果是这样,如果我必须在派生类中重新实现相同的方法,那么多重继承的真正意义呢?

1 个答案:

答案 0 :(得分:1)

您的问题似乎与钻石模式无关,但通常与C ++中的继承模型有关。 C ++中的大多数内容都是静态绑定的,因此在编译时编译器会修复使用某个名称的方法或成员:

如果您访问隐式this对象的成员或使用指针或对象的引用,您将最终访问指针或引用在编译时具有的类的成员,无论是否在运行时有一个派生类对象,它具有相同名称的成员。这就是为什么当你在派生类中定义一个相同的命名成员时,他们说你不能覆盖而只是基类中的 shadow 成员。

非虚拟功能也是如此。

行为不同的是虚函数。这些函数可以在派生类中被覆盖,而得到指针到基类的代码可以是基类的虚函数,并最终执行派生类中给出的实现。

所以虚函数的重点不是让编译器在派生类的上下文中重新解释某个函数(正如你似乎理解的那样),而是为了能够用<替换基类中的函数。 em>派生类中的函数。

回到你的动机:如果你想要一个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对象中选择,您需要在D实数代码中创建一个注释,以声明D的唯一get_message,其中可以重用现有函数。