问题:
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++支持多重继承的编程方式
- 多重继承容易带来问题
- 可能出现 “同一个对象的地址不同” 的情况 。
- 虚继承可以解决数据冗余问题
- 虚继承使得架构设计可能出现问题