第54课 被遗弃的多重继承(下)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第54课 被遗弃的多重继承(下)相关的知识,希望对你有一定的参考价值。

1. C++中的多重继承

(1)一个子类可以拥有多个父类

(2)子类拥有所有父类的成员变量

(3)子类继承所有父类的成员函数

(4)子类对象可以当作任意父类对象使用

(5)多重继承的语法规则

    class Derived: public BaseA, public BaseB, public BaseC{…};

2. 多重继承问题一

(1)通过多重继承得到的对象可以拥有“不同的地址”!!!

(2)解释方案:

(3)原因分析

技术分享 

【编程实验】多重继承问题一

#include <iostream>
using namespace std;

class BaseA
{
    int ma;
public:
    BaseA(int a)
    {
        ma = a;
    }
    
    int getA()
    {
        return ma;
    }
};

class BaseB
{
    int mb;
public:
    BaseB(int b)
    {
        mb = b;
    }
    
    int getB()
    {
        return mb;
    }
};

//多继承
class Derived: public BaseA, public BaseB
{
    int mc;
public:
    Derived(int a, int b, int c):BaseA(a),BaseB(b)
    {
        mc = c;
    }
    
    int getC()
    {
        return mc;
    }
    
    void print()
    {
        cout << "ma = " << getA() << ", "
             << "mb = " << getB() << ", "
             << "mc = " << mc << endl;
    }
};

int main()
{  
    cout << "sizeof(Derived) =" << sizeof(Derived) << endl;  //12
    
    Derived d(1, 2, 3);
    
    d.print();
    
    cout << "d.getA() = " << d.getA() << endl;  //1
    cout << "d.getB() = " << d.getB() << endl;  //2
    cout << "d.getC() = " << d.getC() << endl;  //3

    cout << endl;
    
    BaseA* pa = &d; 
    BaseB* pb = &d;//注意以上两行,都是将对象d的地址赋值给一个指针
                   //表面上看似pa指针应该等于pb指针,但实际上不会这样
                   //pa指向的是d对象中BaseA的子对象,而pb指向的是d
                   //对象中BaseB子对象的部分。
    cout << "pa = " << pa << endl;  //0x23fe9c
    cout << "pb = " << pb << endl;  //0x23fea0
    
    void* paa = pa;
    void* pbb = pb;
    
    if(paa == pbb)
    {
        cout <<"point to the same object!" << endl;
    }
    else
    {
        cout << "Error" << endl;   //该行被输出!
    }
    
    cout << "paa = " << paa << endl; //0x23fe9c
    cout << "pbb = " << pbb << endl; //0x23fea0
    
    return 0;
}

3. 多重继承问题二:可能产生冗余的成员

技术分享 

(1)当多重继承关系出现闭合时将产生数据冗余的问题!!!

(2)解决方案:虚继承

  ①虚继承能够解决数据冗余问题

  ②中间层父类不用再关心顶层父类的初始化

  ③最终子类必须直接调用顶层父类的构造函数

(3)存在问题:当架构设计中需要继承时,无法确定使用直接继承还是虚继承!!

【编程实验】多重继承问题二

#include <iostream>
#include <string>

using namespace std;

class People
{
    string mName;
    int mAge;
public:
    People(string name, int age)
    {
        mName = name;
        mAge = age;
    }
    
    void print()
    {
        cout << "Name = " << mName << ", "
             << "Age = " << mAge << endl;
    }
};

//中间类采用虚继承
class Teacher : virtual public People
{
public:
    Teacher(string name,int age): People(name, age)
    {
        
    }
};

//中间类采用虚继承
class Student : virtual public People
{
public:
    Student(string name, int age): People(name, age)
    {
        
    }
};

//博士类(一个博士可能即是老师,又是学生),采用直接继承
//如果中间层的Teacher和Student不采用虚继承的话,那在Doctor
//类中将有来自People类的mName、mAge等成员变量各两份,出现数据
//冗余现象,而且在子类Doctor中,如果直接mName = 1,会出现二义性的错误
//因为编译器不知道mName是来自Teacher类的还是Student类的
class Doctor: public Teacher, public Student
{
public:
    //请注意,构造函数中的最后一个初始化People,也就是说采用虚继承的话,则
    //最终的子终仍需调用顶层父类的构造函数,这也是虚继承的一大问题,因为在
    //实际开发中,有时很难确定最顶层的基类
    Doctor(string name, int age):Teacher(name,age),Student(name,age),People(name,age)
    {
        
    }    
};

int main()
{  
    Doctor d("SantaClaus", 25);
    d.print();
    
    return 0;
}

4. 多重继承可能产生多个虚函数表

技术分享 

【编程实验】多重继承问题三

(1)当类中存在虚函数表时,如果需要进行强制类型转换时,C++中推荐使用新式类型转换关键字!!!

(2)解决方案:dynamic_cast

技术分享 

5. 正确的使用多重继承

(1)工程开发中的“多重继承”方式:单继承+多接口

(2)一些有用的工程建议:

  ①先继承自一个父类,然后实现多个接口

  ②父类提供equal()成员函数,用于判断指针是否指向当前对象

  ③与多重继承相关的强制类型转换用dynamic_cast完成

【编程实验】正确的多继承方式

 

6. 小结

(1)C++支持多重继承的编程方式

(2)多重继承容易带来问题,如“同一个对象的地址不同”“数据冗余”问题等。

(3)多继承中可能出现多个虚函数表指针

(4)与多重继承相关的强制类型转换dynamic_cast完成

(5)工程开发中采用单继承+多接口的方式使用多继承

(6)父类提供成员函数用于判断指针是否指向当前对象

 

以上是关于第54课 被遗弃的多重继承(下)的主要内容,如果未能解决你的问题,请参考以下文章

第53课 被遗弃的多重继承

第53课 被遗弃的多重继承 (下)——正确的使用多重继承

第53课 被遗弃的多重继承 (中)

第53课 被遗弃的多重继承(上)

第53课 被遗弃的多重继承(上)

C++--被遗弃的多重继承经典问题