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

来源:互联网 发布:linux挂载磁盘阵列 编辑:程序博客网 时间:2024/04/28 06:35

多重继承和虚继承

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打给01062332018MP4播放器播放哈利波特在iOS上运行QQ0x7fff09d335200x7fff09d335200x7fff09d335280x7fff09d33530Phone::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:0x7fff86fbd060B:0x7fff86fbd050C:0x7fff86fbd058D:0x7fff86fbd050100200分析:调用构造函数,基类-(成员)-子类,如果多个子类,根据继承顺序表决定构造顺序,this是指针,返回地址   内存结构,按照继承表的顺序,从低到高地址一次排列各个基类子对象基类构造函数先A再B后C子类构造函数D  
1 0