-
多重继承:一个子类可以同时继承多个基类,这样继承方式称为多重继承
向上造型时,编译器会根据各个基类子对象的内存布局,自动进行偏移计算(如,第一个基类子对象的起始地址和整个对象的起始地址一样),以保证指针的类型和所指向的目标基类子对象类型一致。#include <iostream> using namespace std; class Phone{//电话 public: Phone(const string& num):m_num(num){} void call(const string& num){ cout << m_num << "打给" << num << endl; } private: string m_num; }; class Player{//播放器 public: Player(const string& media):m_media(media){} void play(const string& music){ cout << m_media << "播放器播放" << music << endl; } private: string m_media; }; class Computer{//计算机 public: Computer(const string& os):m_os(os){} void run(const string& app){ cout << "在" << m_os << "系统上运行" << app << endl; } private: string m_os; }; //智能手机子类 class SmartPhone: public Phone,public Player,public Computer{ public: SmartPhone(const string& num, const string& media,const string& os) :Phone(num),Player(media),Computer(os){} }; int main(void){ SmartPhone iphone11("13866668888","MP4","Android"); iphone11.call("12315"); iphone11.play("最炫小苹果"); iphone11.run("Angry Bird"); SmartPhone* p1 = &iphone11; Phone* p2 = p1; Player* p3 = p1; Computer* p4 = p1; cout << "p1=" << p1 << endl; cout << "p2=" << p2 << endl; cout << "p3=" << p3 << endl; cout << "p4=" << p4 << endl; cout << "sizeof(string):" << sizeof(string) << endl;// 操作系统及位数不同结果不一样,我Ubuntu 64位,结果32 SmartPhone* p5=static_cast<SmartPhone*>(p4); cout << "p5=" << p5 << endl; return 0; }
结果说明:每个类型都只有一个成员,string,string在我的环境中占32B,所以p3比p2大32,p4比p3大323)名字冲突问题
–》如果一个子类的多个基类存在相同的名字,当通过子类访问这些名字时,编译器会报歧义错误–名字冲突。
–》解决名字冲突的通用方法就是在前面加上“类名::”显式指明所访问的成员属于哪个基类//推荐
–》如果产生冲突是成员函数,并满足参数不同的重载要求,也可以通过using声明,让它们在子类中形成重载,通过函数的重载匹配来解决#include <iostream> using namespace std; class Base1{ public: void func(void){ cout << "Base1的func(void)" << endl; } int m_i; }; class Base2{ public: void func(int i){ cout << "Base2的func(void)" << endl; } typedef int m_i; }; class Derived:public Base1,public Base2{ public: //将两个基类中的func都声明到当前子类作用域, //让它们在子类作用域中形成重载 //using Base1::func; //using Base2::func; }; int main(void){ Derived d; d.Base1::func(); d.Base2::func(123); d.Base1::m_i = 100; Derived::Base2::m_i num = 200; cout << d.Base1::m_i << "," << num << endl; return 0; }
-
钻石继承和虚继承
1)一个子类的多个基类源自共同的基类祖先,这样继承结构被称为钻石继承.
A(int m_data)
/ \
B C
\ /
D
2)在创建末端子类(D)对象时,会存在多个公共基类(A)子对象,在通过末端子类访问公共基类的成员,会因为继承不同导致结果不一致.
3)通过虚继承的特殊语法,可以让公共基类(A)子对象实例唯一,并为所有的中间(B、C)类共享,这样即使沿着不同的继承路径,所访问到公共基类的成员也是一致的.
虚继承时,创建末端子类对象继承结果类似如下:
A B C
\|/
D
32位操作系统和下虚继承原理图如下,虚基类表表示对于当前对象来讲,公共基类的偏移量。比如图中B子对象,虚基类表值为8,表示B的地址+8就是A的地址。
4)虚继承语法
–》在继承表中使用virtual关键字修饰
–》位于继承链最末端的子类负责构造公共基类子对象#include <iostream> using namespace std; /* A //公共基类 * / \ * B C //中间类 * \ / * D //末端子类 * */ class A{ public: A(int data):m_data(data){ cout << "A:" << this << "," << sizeof(A) << endl; } protected: int m_data; }; class B:virtual public A{//虚继承 public: B(int data):A(data){ cout << "B:" << this << "," << sizeof(B) << endl; } void set(int data){ m_data = data; } }; class C:virtual public A{//虚继承 public: C(int data):A(data){ cout << "C:" << this << "," << sizeof(C) << endl; } int get(void){ return m_data; } }; class D:public B,public C{ public: //虚继承时由末端子类负责构造公共基类子对象 D(int data):B(data),C(data),A(data){ cout << "D:" << this << "," << sizeof(D) << endl; } }; int main(void){ D d(100); cout << d.get() << endl;//100 d.set(200); cout << d.get() << endl;//200 cout << "sizeof(d):" << sizeof(d) << endl;//8 return 0; }
结果分析:A,B,C,D相对关系如代码上边的图,那个图是32位系统的说明图,本机的测试结果是在64位环境下测试的。A的大小为4,因为A里有一个int。B的大小为64位下指针大小加int的大小=8+4=12,由于有8字节大小的指针存在,所以8字节对齐,12变为16;C的大小同理。D的大小是A,B,C之和,注意不能16+16+4,因为不能多次把int相加,B和C里都包含了int,所以应该是B指针8字节加上C指针8字节加上一个int4字节=8+8+4=20,8字节对齐,20变为24.
C++学习之多重继承,钻石继承,虚继承(二)
最新推荐文章于 2023-10-19 14:40:16 发布