问题:
C++中是否允许一个类继承自多个父类呢?答案是肯定的,这种现象就是多重继承。
多重继承是C++中一个特有的特性,因为在其他的程序设计语言里面,如C#、java等语言只支持单重继承
- C++支持编写多重继承的代码
- 一个子类可以拥有多个父类
- 子类拥有所有父类的成员变量
- 子类继承所有父类的成员函数
- 子类对象可以当作任意父类对象使用---赋值兼容也适用于此
多重继承的语法规则:
多重继承的本质与单继承相同!!!
实验:多重继承问题一
1 #include<iostream>
2 #include<string>
3
4 //多重继承带来的问题:(1)多重继承得到的对象可以有“不同的地址”
5 // (2)多重继承可能产生多余的成员
6
7 using namespace std;
8
9 class BaseA
10 {
11 int ma;
12
13 public:
14 BaseA(int a) //构造函数
15 {
16 ma = a;
17 }
18 int getA() //成员函数
19 {
20 return ma;
21 }
22 };
23
24 class BaseB
25 {
26 int mb;
27
28 public:
29 BaseB(int b)
30 {
31 mb = b;
32 }
33 int getB()
34 {
35 return mb;
36 }
37 };
38
39 class Derived : public BaseA, public BaseB
40 {
41 int mc;
42
43 public:
44
45 //单继承------子类的构造函数------初始化列表构造父类的构造函数
46
47 Derived(int a, int b, int c) : BaseA(a), BaseB(b) //多继承--子类的构造函数--初始化列表同时调用两个父类的构造函数
48 {
49 mc = c;
50 }
51
52 int getC()
53 {
54 return mc;
55 }
56
57 void print()
58 {
59 //cout << "ma=" << ma << "," //子类无法直接访问父类的private私有成员-----通过调用父类成员函数(公有的)访问私有成员变量
60 // << "mb=" << mb <<","
61 // << "mc=" << mc <<endl;
62
63
64 //调用父类成员函数--------访问成员变量
65 cout << "ma=" << getA() << ","
66 << "mb=" << getB() << ","
67 << "mc" << mc << endl;
68 }
69 };
70
71 int main()
72 {
73 cout << "sizeof(Derived)= " << sizeof(Derived) << endl; // 3*4=12 子类内存空间大小:父类与子类数据成员的叠加ma mb mc
74 //增加子类成员函数后,内存空间大小还是12
75 //类里面成员函数和成员变量是分开存放的,
76 //每个类有自己的成员变量,都有类对象共享类的成员函数
77
78 //继承构造函数调用顺序:先父母(BaseA(a), BaseB(b) ),再客人(成员变量是其他类的对象--调用成员对象的构造函数),最后自己mc = c;
79 Derived d(1, 2, 3);
80
81 d.print(); //ma =1 ,mb =2 ,mc=3
82
83 //继承两个父类成员函数
84 cout << "d.getA()=" << d.getA() << endl; //d.getA()=1
85 cout << "d.getB()=" << d.getB() << endl; //d.getB()=2
86 cout << "d.getC()=" << d.getC() << endl; //d.getC()=3
87
88 cout << endl;
89
90 //赋值兼容---父类指针pa pb同时指向子类对象d(合法)
91 BaseA* pa = &d;
92 BaseB* pb = &d;
93
94 cout << "pa->getA()=" << pa->getA() << endl; //pa->getA()=1
95 cout << "pb->getB()=" << pb->getB() << endl; //pb->getB()=2
96 cout << endl;
97
98
99 //新定义指针paa pbb同时指向相同对象d
100 void* paa = pa;
101 void* pbb = pb;
102
103 if (paa == pbb) //指针保存的地址值,
104 {
105 cout << "pointer to the same object! " << endl;
106 }
107 else
108 {
109 cout << "error" << endl; //error
110 }
111
112 cout << "pa= " << pa << endl; //pa=0xbfe7e304
113 cout << "pb= " << pb << endl; //pb=0xbfe7e308
114 cout << "paa= " << paa << endl; //paa=0xbfe7e304
115 cout << "pbb= " << pbb << endl; //pbb=0xbfe7e308
116
117 //父类指针指向子类变量 pa,pb保存的地址值为什么不同??????整个期间也没有改变指针值??
118 //答:多重继承得到的对象可以有“不同的地址”
119
120
121 return 0;
122 }
分析多重继承:
多重继承的问题一:
pa、pb指针还是指向一个对象,只是指向的位置不同----pa指向头部,pb可能指向尾部------不方便程序开发
实际开发中,判断两个指针是不是指向同一个对象,是根据比较两个指针保存的地址值判断。
现在是两个指针保持的地址值不同,但是确实是指向同一个对象--------没有方案处理这个问题
多重继承的问题二:
现实生活中一个人可能扮演两个角色-------(老师,学生)
一个Doctor继承Teacher和Student (Teacher和Student分别拥有m_name、m_age),那么此时出现Doctor有两个m_name、m_age---多余成员产生
实验:代码描述上面类图
1 #include<iostream>
2 #include<string>
3
4 using namespace std;
5
6 class people
7 {
8 string m_name;
9 int m_age;
10 public:
11 people(string name, int age)
12 {
13 m_name = name;
14 m_age = age;
15 }
16 void print()
17 {
18 cout << "name= " << m_name << ","
19 << "age= " << m_age << endl;
20 }
21 };
22
23 class teacher : public people
24 {
25 public:
26 teacher(string name, int age) : people(name, age) //调用父类构造函数
27 {
28 }
29 };
30
31 class student : public people
32 {
33 public:
34 student(string name, int age) : people(name, age)
35 {
36 }
37 };
38
39
40 //多重继承---------带来的数据冗余
41 class doctor : public student, public teacher
42 {
43 public:
44 //doctor(string name, int age) : student(name, age), teacher(name, age)
45 doctor(string name, int age) : student(name+"1", age+1), teacher(name+"2", age+2)
46 {
47 }
48 };
49
50 int main()
51 {
52 doctor d("mimi", 26);
53
54 //d.print(); //由于doctor继承了两个类student和teacher,所以对应doctor有两个print函数,编译器不知道调用哪个
55 //使用类作用域分辨符调用print();手动调用
56
57
58 //多重继承---------带来的数据冗余
59 d.teacher::print(); // name =mimi,age =26
60 d.student::print(); // name =mimi,age =26
61
62 //修改后
63 d.teacher::print(); // name =mimi1,age =27
64 d.student::print(); // name =mimi1,age =27
65
66 return 0;
67 }
中间父类加Virtual关键字:
- 虚继承能够解决数据冗余问题
- 中间层父类不再关心顶层父类的初始化------顶层父类的初始化由子类完成
- 最终子类必须直接调用顶层父类的构造函数
虚继承发生----Teacher和Student都不再调用父类People的构造函数,而是让Doctor调用
程序说明:
1 #include<iostream>
2 #include<string>
3
4 //解决多重继承带来的数据冗余--------->>虚继承--------中间父类加virtual关键字
5 using namespace std;
6
7 class people
8 {
9 string m_name;
10 int m_age;
11 public:
12 people(string name, int age)
13 {
14 m_name = name;
15 m_age = age;
16 }
17 void print()
18 {
19 cout << "name= " << m_name << ","
20 << "age= " << m_age << endl;
21 }
22 };
23
24 class teacher : virtual public people
25 {
26 public:
27 teacher(string name, int age) : people(name, age) //调用父类构造函数
28 {
29 }
30 };
31
32 class student :virtual public people
33 {
34 public:
35 student(string name, int age) : people(name, age)
36 {
37 }
38 };
39
40 class doctor : public student, public teacher
41 {
42 public:
43 doctor(string name, int age) : student(name, age), teacher(name, age) ,people(name,age) //项目工程大的时候找顶层父类是很困难的
44 {
45 }
46 };
47
48 int main()
49 {
50 doctor d("mimi", 26);
51
52 d.print(); // name =mimi,age =26 技术上解决多重继承带来的数据冗余,但是项目工程大的时候找顶层父类是很困难的
53
54
55 //d.teacher::print(); // name =mimi,age =26
56 //d.student::print(); // name =mimi,age =26
57
58 return 0;
59 }
技术上解决多重继承带来的数据冗余,但是项目工程大的时候找顶层父类是很困难的-------增加了项目开发时间,代码复杂性增加
虚继承在技术方面确实解决了数据冗余问题,但是它使程序变得过于复杂,不利于工程方面的应用。
如果在项目中引进多重继承,当架构设计中需要继承时,无法确定使用直接继承还是虚继承。
注意:多继承只适合做学术研究,不适合做工程开发
小结:
- C++支持多重继承的编程方式
- 多重继承容易带来问题
- 可能出现 “同一个对象的地址不同” 的情况 。
- 虚继承可以解决数据冗余问题
- 虚继承使得架构设计可能出现问题