C++学习之多重继承,钻石继承,虚继承(二)

  1. 多重继承:一个子类可以同时继承多个基类,这样继承方式称为多重继承
     
    向上造型时,编译器会根据各个基类子对象的内存布局,自动进行偏移计算(如,第一个基类子对象的起始地址和整个对象的起始地址一样),以保证指针的类型和所指向的目标基类子对象类型一致。

    #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大32

    3)名字冲突问题
    –》如果一个子类的多个基类存在相同的名字,当通过子类访问这些名字时,编译器会报歧义错误–名字冲突。
    –》解决名字冲突的通用方法就是在前面加上“类名::”显式指明所访问的成员属于哪个基类//推荐
    –》如果产生冲突是成员函数,并满足参数不同的重载要求,也可以通过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;
    }
    
  2. 钻石继承和虚继承
    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的地址。
    32位操作系统和下虚继承原理图
    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.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值