C++【多重继承和虚继承】

多重继承和虚继承

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  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值