看如下代码:
1 #include <iostream> 2 3 using namespace std; 4 5 class A { 6 public: 7 A() {}; 8 9 protected: 10 int m_a; 11 }; 12 13 class B : public A 14 { 15 public: 16 B(){}; 17 18 protected: 19 int m_b; 20 }; 21 22 class C : public A 23 { 24 public: 25 C(){}; 26 27 protected: 28 int m_c; 29 }; 30 31 class D : public B, public C 32 { 33 public: 34 D() {}; 35 void setA(int a) { 36 m_a = a; //Compile error 37 } 38 void setB(int b) { 39 m_b = b; 40 } 41 void setC(int c) { 42 m_c = c; 43 } 44 void setD(int d) { 45 m_d = d; 46 } 47 48 private: 49 int m_d; 50 }; 51 52 53 54 55 int main() 56 { 57 std::cout << "Hello World!\n"; 58 }
在36行处会出现错误,因为D类不知道m_a是从B类继承下来还是C类继承下来的,即出现命名冲突,m_a不明确.
为了解决多继承时的命名冲突和冗余数据问题,C++ 提出了虚继承,使得在派生类中只保留一份间接基类的成员。
在继承方式上加上virtual关键字就是虚继承,如下:
1 #include <iostream> 2 3 using namespace std; 4 5 class A { 6 public: 7 A() {}; 8 9 protected: 10 int m_a; 11 }; 12 13 class B : virtual public A 14 { 15 public: 16 B(){}; 17 18 protected: 19 int m_b; 20 }; 21 22 class C : virtual public A 23 { 24 public: 25 C(){}; 26 27 protected: 28 int m_c; 29 }; 30 31 class D : public B, public C 32 { 33 public: 34 D() {}; 35 void setA(int a) { 36 m_a = a; 37 } 38 void setB(int b) { 39 m_b = b; 40 } 41 void setC(int c) { 42 m_c = c; 43 } 44 void setD(int d) { 45 m_d = d; 46 } 47 48 private: 49 int m_d; 50 }; 51 52 53 54 55 int main() 56 { 57 std::cout << "Hello World!\n"; 58 }
此时D只保留了一份m_a,直接访问就不会出现歧义了.
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 A 就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。
虚派生只影响从指定了虚基类的派生类中进一步派生出来的类,它不会影响派生类本身。
虚基类成员的可见性
因为在虚继承的最终派生类中只保留了一份虚基类的成员,所以该成员可以被直接访问,不会产生二义性。此外,如果虚基类的成员只被一条派生路径覆盖,那么仍然可以直接访问这个被覆盖的成员。但是如果该成员被两条或多条路径覆盖了,那就不能直接访问了,此时必须指明该成员属于哪个类。
以例子中的菱形继承为例,假设 B 定义了一个名为 x 的成员变量,当我们在 D 中直接访问 x 时,会有三种可能性:
- 如果 B 和 C 中都没有 x 的定义,那么 x 将被解析为 B 的成员,此时不存在二义性。
- 如果 B 或 C 其中的一个类定义了 x,也不会有二义性,派生类的 x 比虚基类的 x 优先级更高。
- 如果 B 和 C 中都定义了 x,那么直接访问 x 将产生二义性问题。
可以看到,使用多继承经常会出现二义性问题,必须十分小心。上面的例子是简单的,如果继承的层次再多一些,关系更复杂一些,程序员就很容易陷人迷魂阵,程序的编写、调试和维护工作都会变得更加困难,因此我不提倡在程序中使用多继承,只有在比较简单和不易出现二义性的情况或实在必要时才使用多继承,能用单一继承解决的问题就不要使用多继承。也正是由于这个原因,C++ 之后的很多面向对象的编程语言,例如 Java、C#、PHP等,都不支持多继承。
转载自:http://c.biancheng.net/view/2280.html
原文地址:https://www.cnblogs.com/ArrowToLanguage/p/12290172.html