C 的多继承有点恶心了~

大家好,我是情报小哥!

01

体验多继承

继承这种语言特性是非常有魅力的,没有继承真的不敢称自己是在进行面向对象编程,很多朋友玩C语言炉火纯青,想进行面向对象编程,那肯定离不开模拟继承这一属性。

世间万物得以生生不息,在于不断的繁衍与继承,所谓“一生二,二生三......”,也就说明了类似于继承这种语言特性的重要性,这些也是为什么C++语言一直泛发活力的重要原因。

前面小哥跟大家介绍过了继承,那时候的子类只继承了一个父类,也叫单继承,而C++语言是可以支持多继承的,这样也就给编程带来了一定的复杂度,这也是符合我们现实生活的对象情景的,最直接的就是,每个人都既继承了父亲特点,又继承了来自母亲的特质。

那么我们先来看一下多继承的小例子:

1#include <iostream>
2using namespace std;
3
4class mother
5{
6public:
7    int Val;
8
9public:
10    void print(void)
11    {
12        cout<<'mother'<<endl; 
13    } 
14};
15
16class father
17{
18public:
19    int Val;
20
21public:
22    void print(void)
23    {
24        cout<<'father'<<endl; 
25    } 
26};
27
28class child: public  mother,public father
29{
30public:
31    int Val;
32
33};
34
35int main(int argc, char** argv) {
36
37    child  child;
38
39    child.mother::print(); 
40    child.father::print(); 
41
42    return 0;
43}

子类分别公有继承父类mother和father,如果此时通过子类访问所继承的成员在两个父类中都有定义,这样就存在同名成员的二义性,编译器会报错。

就像上面的例子如果直接调用child.print();编译器就会报错。当然你可以像上面例子中那样通过命令空间进行标识,从而告诉编译器具体所要访问的成员,但是这样属实有点繁琐,如果继承关系再复杂一点又加重了程序的复杂度。

02

菱形问题

多继承虽然符合我们实际的思维,不过多继承存在本身的复杂度,单继承可以看成是一棵树,逻辑上比较单一,而有了多继承就不一样了,继承关系可以复杂成一个蜘蛛网,特别是多继承的菱形继承问题,属实麻烦。
 1#include <iostream> 2using namespace std; 3 4class obj  5{ 6public: 7    int Val; 8 9public:10    obj() //构造 11    {12        Val = 10;13    }1415    void print(void)16    {17        cout<<'mother'<<endl; 18    } 19};2021class obj1 :  public obj22{23public:24    //25public:26    void print(void)27    {28        cout<<'obj1'<<endl; 29    } 30};3132class obj2 :  public obj 33{34public:35    //36public:37    void print(void)38    {39        cout<<'obj2'<<endl; 40    } 41};4243class Child:public obj1,public obj2 44{45public:4647public:48    void print(void)49    {50        cout<<'Child'<<endl; 51    } 52};5354int main(int argc, char** argv) {5556    Child  child;5758    //cout<<'Val:'<<&child.Val<<endl;  //菱形问题存在二义性,编译不通过 5960    cout<<'Val:'<<&child.obj1::Val<<endl; 61    cout<<'Val:'<<&child.obj2::Val<<endl; 6263    return 0;64}
如上的代码就是模拟的菱形继承问题,输出结果如下:
当使用Child对象访问Val变量编译器会报错,由于访问路径不同,存在二义性,这样我们只能通过::命名空间来区分Val存在的双内存,从上面输出的结果看出两个Val地址不同,也可以得到说明。
如果使用多继承,菱形的问题是经常出现的,obj类中的成员变量会分别被继承最终通过Child访问会存在两份内存,对于这两份内存,其实大部分情况下都是多余的,势必造成内存的浪费,又是二义性问题又是浪费内存,C++应该有办法进行解决,那就是虚继承。

03

虚继承

为了解决多继承问题,C++提出了虚继承,就是在继承关系中加入virtual关键字,这样公共继承的类不再存在多份内存,而仅只有一份数据内存,且不存在二义性。
虚继承的做法很简单,就是在继承的共同基类继承关系上加上virtual,如下面的代码所示 :
1#include <iostream>
2using namespace std;
3
4class obj 
5{
6public:
7    int Val;
8
9public:
10    obj() //构造 
11    {
12        Val = 10;
13    }
14
15    void print(void)
16    {
17        cout<<'mother'<<endl; 
18    } 
19};
20
21class obj1 :  virtual public obj
22{
23public:
24    //
25public:
26    void print(void)
27    {
28        cout<<'obj1'<<endl; 
29    } 
30};
31
32class obj2 : virtual public obj 
33{
34public:
35    //
36public:
37    void print(void)
38    {
39        cout<<'obj2'<<endl; 
40    } 
41};
42
43class Child:public obj1,public obj2 
44{
45public:
46
47public:
48    void print(void)
49    {
50        cout<<'Child'<<endl; 
51    } 
52};
53
54int main(int argc, char** argv) {
55
56    Child  child;
57
58    cout<<'Val:'<<&child.Val<<endl; 
59    cout<<'Val:'<<&child.obj1::Val<<endl; 
60    cout<<'Val:'<<&child.obj2::Val<<endl; 
61
62    return 0;
63}
这样派生出来的Child类只会存在一份obj类数据,所以也可以直接通过child对基类Val进行访问,且不会产生二义性,我们叫这种继承方式为虚继承,同时称共享的基类obj为虚基类。
这样看起来多继承也不是那么难,然而随着继承关系的复杂,多继承用起来也会变得难以分析,所以在一般的开发中都会使用单继承,少量简单的继承关系才会使用到多继承~

最  后 

这里小哥就介绍了一下C++中多继承的使用和虚继承等的特点,希望本文能够对你有帮助,该系列还会持续更新,大家可以持续关注~

(0)

相关推荐