多重继承
-
C++支持编写多重继承的代码
-
一个子类可以拥有多个父类
-
子类拥有父类的成员变量
-
子类继承所有父类的成员变量
-
子类对象可以当作任意父类对象使用
-
多重继承的语法规则
class Derived : public BaseA,public BaseB,public BaseC { };//多重继承的本质与单继承相同
-
多重继承问题一(对象地址不同)
#include <iostream> #include <string> using namespace std; class BaseA { int ma; public: BaseA(int a) { ma = a; } int getma() { return ma; } }; class BaseB { int mb; public: BaseB(int b) { mb = b; } int getmb() { return mb; } }; //多重继承 class Derived :public BaseA,public BaseB { public: int mc; Derived(int a,int b,int c) :BaseA(a),BaseB(b) { mc = c; } void print() { cout << "ma = " << getma() << ", " << "mb = " << getmb() << ", " << "mc = " << mc << endl; } }; int main() { Derived d(1,2,3); d.print(); BaseA* pa = &d; BaseB* pb = &d; cout << "pa->getma() = " << pa->getma() << endl; cout << "pb->getmb() = " << pb->getmb() << endl; //发现问题:地址是不同的 cout << "pa = " << &pa << endl; cout << "pb = " << &pb << endl; }
-
运行结果
ma = 1, mb = 2, mc = 3 pa->getma() = 1 pb->getmb() = 2 pa = 0073FAF4 pb = 0073FAE8
通过程序可知,pa,pb明明指向同一个对象d,但是地址却不同。
通过多重继承得到的对象可能拥有“不同的地址”!!无解决方案
-
多重继承问题二(可能产生冗余的成员)
#include <iostream> #include <string> using namespace std; class People { string name; int age; public: People(string _name,int _age) { name = _name; age = _age; } void print() { cout << "my name is " << name << endl; cout << "my age is " << age << endl; } }; class Teacher : public People { public: Teacher(string _name,int _age) : People(_name,_age) { } }; class Student : public People { public: Student(string _name, int _age) : People(_name, _age) { } }; class Doctor : public Teacher,public Student { public: Doctor(string name, int age) : Teacher(name, age), Student(name, age) { } }; int main() { Doctor d("chenge",22); d.print(); }
-
运行结果
Doctor继承与Teacher,Student,而Teacher和studebt都继承与People,这也就导致编译器在编译时不知道选择哪一个print函数,到底是Teacher中的,还是Student中的呢?当多重继承关系闭合时,将产生数据冗余的问题!!!
解决方法:虚继承
class People{}; class Teacher : virtual public People{}; class Student : virtual public People{}; class Doctor : public Teacher,public Student{};
-
虚继承能够解决数据冗余问题
-
中间层父类不再关心顶层父类的初始化
-
最终子类必须直接调用顶层父类的构造函数
#include <iostream> #include <string> using namespace std; class People { string name; int age; public: People(string _name,int _age) { name = _name; age = _age; } void print() { cout << "my name is " << name << endl; cout << "my age is " << age << endl; } }; //虚继承 class Teacher : virtual public People { public: Teacher(string _name,int _age) : People(_name,_age) { } }; //虚继承 class Student : virtual public People { public: Student(string _name, int _age) : People(_name, _age) { } }; class Doctor : public Teacher,public Student { public: Doctor(string name, int age) : Teacher(name, age), Student(name, age),People(name,age) { } }; int main() { Doctor d("chenge",22); d.print(); }
-
运行结果
-
多重继承问题三(可能产生多个虚函数表)
#include <iostream> #include <string> using namespace std; class BaseA { public: virtual void printa() { } }; class BaseB { public: virtual void printb() { } }; class Derived :public BaseA,public BaseB { }; int main() { Derived d; cout << "sizeof(d)="<< sizeof(d) << endl; }
-
运行结果
因为我在ubuntu64位上运行的,所以一个虚函数表地址占8个字节
#include <iostream> #include <string> using namespace std; class BaseA { public: virtual void printa() { cout << "BaseA::printa()" << endl; } }; class BaseB { public: virtual void printb() { cout << "BaseB::printb()" << endl; } }; class Derived :public BaseA,public BaseB { }; int main() { Derived d; cout << "sizeof(d)="<< sizeof(d) << endl; BaseA* pa = &d; BaseB* pb = &d; cout << "pa = " << pa << endl; cout << "pb = " << pb << endl; pa->printa(); pb->printb(); BaseB* pbe = (BaseB*)pa; pbe->printb(); BaseB* pbc = dynamic_cast<BaseB*>(pa); pbc->printb(); }
-
运行结果(在vs2019上测试的)
可以发现,如果使用BaseB*对指针pa使用普通的类型转换方式进行强制类型,输出结果并不是我们所期望的,在需要进行强制类型转换时,C++中推荐使用新式类型转换关键字。解决方案:dynamic_cast
-
工程开发中的“多重继承”方式:单继承某个类+实现(多个)接口
#include <iostream> #include <string> using namespace std; class Base { protected: int mi; public: Base(int i) { mi = i; } int getI() { return mi; } bool equal(Base* obj) { return (this == obj); } }; class Interface1 { public: virtual void add(int i) = 0; virtual void minus(int i) = 0; }; class Interface2 { public: virtual void multiply(int i) = 0; virtual void divide(int i) = 0; }; class Derived : public Base, public Interface1, public Interface2 { public: Derived(int i) : Base(i) { } void add(int i) { mi += i; } void minus(int i) { mi -= i; } void multiply(int i) { mi *= i; } void divide(int i) { if (i != 0) { mi /= i; } } }; int main() { Derived d(100); Derived* p = &d; Interface1* pInt1 = &d; Interface2* pInt2 = &d; cout << "p->getI() = " << p->getI() << endl; // 100 pInt1->add(10); pInt2->divide(11); pInt1->minus(5); pInt2->multiply(8); cout << "p->getI() = " << p->getI() << endl; // 40 cout << endl; cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl; cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl; return 0; }
-
运行结果
-
一些有用的建议
-
先继承自一个父类,然后实现多个接口
-
父类中提供equal()成员函数
-
equal()成员函数用于判断指针是否指向当前对象
-
与多重继承相关的强制类型转换用dynamic_cast完成
-
小结:
-
C++支持多重继承的编程方式
-
多重继承容易带来问题:可能出现“同一个对象的地址不同”的情况,虚继承可以解决数据冗余问题,虚继承使得架构设计可能出现问题
-
多继承中可能出现多个虚函数表指针
-
与多重继承相关的强制类型转换用dynamic_cast完成
-
工程开发中采用“单继承多接口”的方式使用多继承
-
父类提供成员函数用于判断指针是否指向当前对象
主要记录的是学习听课的笔记