C++虚继承和虚基类

看如下代码:

 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++ 之后的很多面向对象的编程语言,例如 JavaC#PHP等,都不支持多继承。

转载自:http://c.biancheng.net/view/2280.html

原文地址:https://www.cnblogs.com/ArrowToLanguage/p/12290172.html

时间: 2024-10-29 19:10:18

C++虚继承和虚基类的相关文章

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承、虚继承与虚基类

C++ Primer学习笔记32_面向对象编程(3)--继承(三):多重继承.虚继承与虚基类 一.多重继承 在C++语言中,一个派生类可以从一个基类派生,称为单继承:也可以从多个基类派生,称为多继承. 多重继承--一个派生类可以有多个基类 class <派生类名> : <继承方式1> <基类名1>,<继承方式2> <基类名2>,... { <派生类新定义成员> }; 可见,多继承与单继承的区别从定义格式上看,主要是多继承的基类多于一个

C++ 纯虚函数与抽象基类

在C++ 中,虚函数可以很好的实现多态,然而也有一定的局限,这个局限在于,虚函数必须在基类中定义,哪怕是空的实现. 例如下面这段代码编译时就会出错: class Base { virtual void print(); }; class Derived:Base { void print() { cout << "Derived" <<endl; } }; 因为Base类中的虚函数print()没有定义. 而在实际编程时我们往往会遇到无法给基类的函数定义的情况,

纯虚函数与抽象基类

这一节我们主要介绍一下抽象基类与纯虚函数的关系 首先,我们来看看纯虚函数 1.纯虚函数的定义   纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法.在基类中实现纯虚函数的方法是在函数原型后加"=0".如下     virtual void funtion()=0 2.定义纯虚函数的目的和意义 定义纯虚函数是为了实现一个接口,起到一个规范的作用.纯虚函数的意义在于:让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但类无法为纯虚函数提供

虚函数的使用 以及虚函数与重载的关系, 空虚函数的作用,纯虚函数-&gt;抽象类,基类虚析构函数使释放对象更彻底

为了访问公有派生类的特定成员,可以通过讲基类指针显示转换为派生类指针. 也可以将基类的非静态成员函数定义为虚函数(在函数前加上virtual) 1 #include<iostream> 2 using namespace std; 3 4 class base{ 5 public: 6 /*virtual*/ void who(){ //define this function to virtual will be normal 7 cout << "this is th

C++ 虚继承和虚函数同时存在的对象模型

如果说没有虚函数的虚继承只是一个噩梦的话,那么这里就是真正的炼狱.这个C++中最复杂的继承层次在VC上的实现其实我没有完全理解,摸爬滚打了一番也算得出了微软的实现方法吧,至于一些刁钻的实现方式我也想不到什么理由来解释它,也只算是知其然不知其所以然吧. 分2个阶段来探讨: 1.      有虚函数的派生类虚继承了没有虚函数的基类: 2.      有虚函数的派生类虚继承了有虚函数的基类: 1.  基类无虚函数 1.1 虚.实基类都没有虚函数 这种情况也还算比较简单.因为虚函数表指针一定是会放在最开

C++多重继承中的虚继承和虚函数举例

上一篇虚继承举例:http://10638473.blog.51cto.com/10628473/1964414 本文将A类中的show()函数前加上virtual关键字. //多重继承 #include <iostream> using namespace std; class A { public:     int a;     A(int a=0):a(a)     {         cout<<"A基类A::A()"<<endl;     

虚继承和虚函数继承

虚继承主要用于菱形 形式的继承形式 虚继承是为了在多继承的时候避免引发歧义, 比如类A有个就是a,B继承了A,C也继承了A,当D多继承B,C时,就会有歧义产生了,所以要使用虚拟继承避免重复拷贝. 虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联 虚函数继承: class A { virtual void fun() {cout < <'A' < <endl;}; }; class B : pub

虚继承与虚函数继承

虚继承主要用于菱形 形式的继承形式. 虚继承是为了在多继承的时候避免引发歧义,比如类A有个就是a,B继承了A,C也继承了A,当D多继承B,C时,就会有歧义产生了,所以要使用虚拟继承避免重复拷贝.虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联 虚函数继承:class A{    virtual void fun() {cout < <'A' < <endl;};};class B : publi

简单工厂类,有继承、虚函数重写、基类指针指向子类对象会发生多态(1)

class Fruit { public: virtual void get_fruit() { std::cout<<"Fruit:getFruit"<<std::endl; } }; class Pear : public Fruit { public: virtual void get_fruit() { std::cout<<"Pear:getFruit"<<std::endl; } }; class Bana