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
,其中可以复用已有的函数。
我正在学习 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
,其中可以复用已有的函数。