C++【多重继承和虚继承】
2015-06-16 12:02
281 查看
多重继承和虚继承
1)定义
子类同时拥有两个或两个以上的基类,同时继承了所有基类的属性和行为。
销售员 经理
\ /
销售经理
汽车 客用特性 商用特性
\ | /
公共汽车
2)内存结构
按照继承表的顺序,从低到高地址一次排列各个基类子对象。将子类对象的地址隐式或者静态转换为基类指针,编译器会做地址计算,以保证基类指针的类型和其所指向的对象一致,但是重解释类型转换(reinterpret_cast)不做此计算。
3)防止名字冲突
由用户程序通过作用域限定解决冲突,或者借助using声明以重载的方式解决冲突,再或者通过汇聚替代,在汇聚子类中提供对产生冲突的函数的隐藏版本,并在该隐藏版本中通过正确的逻辑选择特定基类的实现。
示例代码:
4)钻石继承问题
公共基类子对象在最终子类对象中存在多个实例,沿着不同的继承路径所访问到的公共基类子对象可能不一样,由此导致数据不一致问题。
A
/ \
B C
\ /
D
为了解决钻石继承问题,需要设法让公共基类子对象在最终子类对象中仅有唯一的实例,且为所有中间子类对象所共享——虚继承。
示例代码:
1)定义
子类同时拥有两个或两个以上的基类,同时继承了所有基类的属性和行为。
销售员 经理
\ /
销售经理
汽车 客用特性 商用特性
\ | /
公共汽车
2)内存结构
按照继承表的顺序,从低到高地址一次排列各个基类子对象。将子类对象的地址隐式或者静态转换为基类指针,编译器会做地址计算,以保证基类指针的类型和其所指向的对象一致,但是重解释类型转换(reinterpret_cast)不做此计算。
3)防止名字冲突
由用户程序通过作用域限定解决冲突,或者借助using声明以重载的方式解决冲突,再或者通过汇聚替代,在汇聚子类中提供对产生冲突的函数的隐藏版本,并在该隐藏版本中通过正确的逻辑选择特定基类的实现。
示例代码:
#include <iostream> using namespace std; class Phone { public: Phone (const string& numb) : m_numb (numb) {} void call (const string& numb) const { cout << m_numb << "打给" << numb << "。" << endl; } void foo (void) { cout << "Phone::foo()" << endl; } void bar (void) { cout << "Phone::bar()" << endl; } private: string m_numb; }; class Player { public: Player (const string& media) : m_media (media) {} void play (const string& clip) const { cout << m_media << "播放器播放" << clip << "。" << endl; } void foo (int n) { cout << "Player::foo()" << endl; } void bar (void) { cout << "Player::bar()" << endl; } private: string m_media; }; class Computer { public: Computer (const string& os) : m_os (os) {} void run (const string& prog) const { cout << "在" << m_os << "上运行" << prog << "。" << endl; } private: string m_os; }; class SmartPhone : public Phone, public Player, public Computer { public: SmartPhone (const string& numb, const string& media, const string& os) : Phone (numb), Player (media), Computer (os) {} using Phone::foo;//防止名字冲突,借助using声明以重载的方式解决冲突 using Player::foo;//防止名字冲突,借助using声明以重载的方式解决冲突 // using Phone::bar; // using Player::bar; void bar (bool flag) { if (flag) Phone::bar (); else Player::bar (); } }; int main (void) { SmartPhone sp ("13910110072", "MP4", "iOS"); sp.call ("01062332018"); sp.play ("哈利波特"); sp.run ("QQ"); cout << &sp << endl; Phone* p1 = &sp;//指向子类对象的基类指针 cout << p1 << endl; Player* p2 = &sp; cout << p2 << endl; // Computer* p3 = static_cast<Computer*> (&sp);//将子类对象的地址隐式或者静态转换为基类指针 Computer* p3 = reinterpret_cast<Computer*> (&sp);//但是重解释类型转换(reinterpret_cast)不做此计算 cout << p3 << endl;//0x7fff09aaabe0 /* sp.Phone::foo ();//防止名字冲突由用户通过作用域限定解决冲突 sp.Player::foo (10); */ sp.foo (); sp.foo (10); sp.bar (true); sp.bar (false); return 0; } *输出结果*: 13910110072打给01062332018 MP4播放器播放哈利波特 在iOS上运行QQ 0x7fff09d33520 0x7fff09d33520 0x7fff09d33528 0x7fff09d33530 Phone::foo() Player::foo() Phone::bar() Player::bar()
4)钻石继承问题
公共基类子对象在最终子类对象中存在多个实例,沿着不同的继承路径所访问到的公共基类子对象可能不一样,由此导致数据不一致问题。
A
/ \
B C
\ /
D
为了解决钻石继承问题,需要设法让公共基类子对象在最终子类对象中仅有唯一的实例,且为所有中间子类对象所共享——虚继承。
示例代码:
#include <iostream> using namespace std; class A { public: A (int data) : m_data (data) { cout << "A:" << this << endl; } protected: int m_data; }; class B : virtual public A { public: B (int data) : A (data) { cout << "B:" << this << endl; } void set (int data) { m_data = data; } }; class C : virtual public A { public: C (int data) : A (data) { cout << "C:" << this << endl; } int get (void) { return m_data; } }; class D : public B, public C { public: D (int data) : B (-1), C (-2), A (data) { cout << "D:" << this << endl; } }; int main (void) { D d (100); cout << d.get () << endl;//100 d.set (200); cout << d.get () << endl;//200 return 0; } 输出结果: A:0x7fff86fbd060 B:0x7fff86fbd050 C:0x7fff86fbd058 D:0x7fff86fbd050 100 200 分析: 调用构造函数,基类-(成员)-子类,如果多个子类,根据继承顺序表决定构造顺序,this是指针,返回地址 内存结构,按照继承表的顺序,从低到高地址一次排列各个基类子对象 基类构造函数先A再B后C 子类构造函数D
相关文章推荐
- 错误的结果 -1073741819 从"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\mt.exe"返回
- C++11变长模板解析(深入理解C++11)
- 学习笔记 C++ const放在函数体前和函数参数列表之后的作用
- C++【浅谈虚析构函数】
- C++对二进制文件的读写操作
- 注释转换(C++到C)
- C++语法总结,语法查询
- C++vector
- C++静态库与动态库
- 《C++语言基础》实践参考——max带来的冲突
- 《C++语言基础》实践参考——有些数的阶乘不算了
- 《C++语言基础》实践参考——平方根中的异常
- 《C++语言基础》实践项目——异常处理和命名空间
- C++快捷键
- 《C++语言基础》程序阅读——异常处理和命名空间
- C++ 将文件夹中文件写入list.txt文件中
- C++ - C++0x/C++11 Support in GCC
- C++运算符重载的实现
- NYOJ 86 找球号(一)
- C++ 程序员要像医生一样调试代码?