31、不一样的C++系列--多重继承
来源:互联网 发布:mac黄皮适合口红颜色 编辑:程序博客网 时间:2024/05/17 04:31
多重继承
- C++支持编写多重继承的代码
- 一个子类可以拥有多个父类
- 子类拥有所有父类的成员变量
- 子类继承所有父类的成员函数
- 子类对象可以当作任意父类对象使用
- 多重继承的语法规则
//多重继承的本质与单继承相同class Derived : public BaseA, public BaseB, public BaseC{ // ......};
多重继承的问题一
这里首先看一段代码:
#include <iostream>#include <string>using namespace std;//定义父类BaseAclass BaseA{ //成员变量 int ma;public: //带参构造函数 BaseA(int a) { ma = a; } //成员函数 int getA() { return ma; }};//定义父类BaseBclass BaseB{ //成员变量 int mb;public: //带参构造函数 BaseB(int b) { mb = b; } //成员函数 int getB() { return mb; }};//子类Derived同时继承BaseA 和 BaseBclass Derived : public BaseA, public BaseB{ //子类的成员变量 int mc;public: //子类带参构造函数 初始化列表中初始化2个父类 Derived(int a, int b, int c) : BaseA(a), BaseB(b) { mc = c; } //成员函数 int getC() { return mc; } //成员函数 void print() { cout << "ma = " << getA() << ", " << "mb = " << getB() << ", " << "mc = " << mc << endl; }};int main(){ cout << "sizeof(Derived) = " << sizeof(Derived) << endl; // 12 Derived d(1, 2, 3); d.print(); cout << "d.getA() = " << d.getA() << endl; cout << "d.getB() = " << d.getB() << endl; cout << "d.getC() = " << d.getC() << endl; cout << endl; return 0;}
上述代码是一个非常标准的多重继承的示例,输出结果如下:
sizeof(Derived) = 12ma = 1, mb = 2, mc = 3d.getA() = 1d.getB() = 2d.getC() = 3
因为子类初始化时在初始化列表中也初始化了2个父类,所以调用父类函数可以输出从父类继承的成员变量。此时,我们再做一点其它操作:
//使用子类对象指针分别赋值给父类指针 BaseA* pa = &d; BaseB* pb = &d; //分别调用函数 cout << "pa->getA() = " << pa->getA() << endl; cout << "pb->getB() = " << pb->getB() << endl; cout << endl; //使用2个指针分别指向父类指针 void* paa = pa; void* pbb = pb; //判断2个指针是否相等 if( paa == pbb ) { cout << "Pointer to the same object!" << endl; } else { cout << "Error" << endl; } //再分别输出4个指针 cout << "pa = " << pa << endl; cout << "pb = " << pb << endl; cout << "paa = " << paa << endl; cout << "pbb = " << pbb << endl;
输出结果为:
sizeof(Derived) = 12ma = 1, mb = 2, mc = 3d.getA() = 1d.getB() = 2d.getC() = 3pa->getA() = 1pb->getB() = 2Errorpa = 0x7fff58957a00pb = 0x7fff58957a04paa = 0x7fff58957a00pbb = 0x7fff58957a04
有没有发现一个奇怪的事情,对子类对象d取地址后分别赋值给2个父类指针,此时竟然会得到2个不同的地址。也就是说 **通过多重继承得到的对象可能拥有不同的地址**
!如下所示:
Derived d(1, 2, 3);BaseA* pa = &d;BaseB* pb = &d;pa ====> int ma; int mb; <==== pb int mc;
多重继承的问题二
接下来再看一段代码:
#include <iostream>#include <string>using namespace std;class People{ string m_name; int m_age;public: People(string name, int age) { m_name = name; m_age = age; } void print() { cout << "Name = " << m_name << ", " << "Age = " << m_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("Delphi", 33); d.print(); return 0;}
在上述代码中,Teacher类和Student类都是People类的子类,然后Doctor类又同时继承Student类和Teacher类,这里会引起歧义。先看下输出结果:
Name = Delphi, Age = 33
看到结果,可以自问一下,d调用print函数,这个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;//定义BaseA类class BaseA{public: //定义虚函数funcA() virtual void funcA() { cout << "BaseA::funcA()" << endl; }};//定义BaseB类class BaseB{public: //定义虚函数funcB() virtual void funcB() { cout << "BaseB::funcB()" << endl; }};//定义Derived类 并同时继承BaseA和BaseB类class Derived : public BaseA, public BaseB{};int main(){ Derived d; //用子类对象d分别赋值给2个父类指针 BaseA* pa = &d; BaseB* pb = &d; //把BaseA指针直接强转为BaseB指针 BaseB* pbe = (BaseB*)pa; //使用dynamic_cast来转换指针 BaseB* pbc = dynamic_cast<BaseB*>(pa); cout << "sizeof(d) = " << sizeof(d) << endl; cout << "Using pa to call funcA()..." << endl; pa->funcA(); cout << "Using pb to call funcB()..." << endl; pb->funcB(); cout << "Using pbe to call funcB()..." << endl; pbe->funcB(); cout << "Using pbc to call funcB()..." << endl; pbc->funcB(); cout << endl; cout << "pa = " << pa << endl; cout << "pb = " << pb << endl; cout << "pbe = " << pbe << endl; cout << "pbc = " << pbc << endl; return 0;}
运行结果如下:
sizeof(d) = 16Using pa to call funcA()...BaseA::funcA()Using pb to call funcB()...BaseB::funcB()Using pbe to call funcB()...BaseA::funcA()Using pbc to call funcB()...BaseB::funcB()pa = 0x7fff59f519f8pb = 0x7fff59f51a00pbe = 0x7fff59f519f8pbc = 0x7fff59f51a00
有没有发现在转换指针时,如果直接进行强制转换,调用的还是原指针指向的方法,也就是说pbe指针虽然是父类指针BaseB类型,但是实际是父类指针BaseA类型,所以调用funcB函数时输出funcA。但如果使用dynamic_cast的时候,就不会存在这个问题。
所以在需要进行强制类型转换时,C++中推荐使用新式类型转换关键字: dynamic_cast
。
正确使用多重继承
- 工程开发中的 “多重继承” 方式:
- 单继承某个类 + 实现(多个)接口
就像这样:
#include <iostream>#include <string>using namespace std;//定义父类Baseclass Base{protected: //成员变量mi,子类可访问,外界不可访问 int mi;public: //带参构造函数 Base(int i) { mi = i; } //成员函数 int getI() { return mi; } //这个函数的作用是判断obj是否是当前对象 bool equal(Base* obj) { return (this == obj); }};//定义接口Interface1class Interface1{public: //2个纯虚函数 virtual void add(int i) = 0; virtual void minus(int i) = 0;};//定义接口Interface2class Interface2{public: //2个纯虚函数 virtual void multiply(int i) = 0; virtual void divide(int i) = 0;};//定义子类 并继承父类Base,并实现2个接口class Derived : public Base, public Interface1, public Interface2{public: //初始化时 初始化Base类 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;}
输出结果为:
p->getI() = 100p->getI() = 40pInt1 == p : 1pInt2 == p : 1
- 一些建议
- 先继承自一个父类,然后实现多个接口
- 父类中提供equal( )成员函数
- equal( ) 成员函数用于判断指针是否指向当前对象
- 与多重继承相关的强制类型转换用 dynamic_cast 完成
阅读全文
0 0
- 31、不一样的C++系列--多重继承
- 26、不一样的C++系列--继承的基础知识
- 28、不一样的C++系列--继承与多态
- (C++)多重继承
- C++--多重继承
- C++-继承:多重继承 && 虚拟继承
- 1、不一样的C++系列--C到C++的升级
- 37、不一样的C++系列--C语言异常处理
- 多重继承的基本概念
- 多重继承的优缺点
- C++的多重继承
- 多重继承的优缺点
- 多重继承的优缺点
- 多重继承的优缺点
- 类的多重继承
- C++的多重继承
- python的多重继承
- python的多重继承
- 国产化网管实现方案
- Integer自动装箱超出赋值范围
- validate 动态验证 根据name值验证
- Java的网络编程
- mtr命令详解诊断网络路由
- 31、不一样的C++系列--多重继承
- import java.***** cannot be resolved
- mysql5.7全/增量备份脚本
- Java全局变量
- 关于url转编码的问题
- android6.0的apk更新
- 在Eclipse中使用5.0之后出现高级控件
- jxbrowser 教程 1 使用jxbrowser 创建简单的浏览器 使用html5+java写cs客户端
- QQ只能输入数字【限制】,不限制数量