多重继承

  • C++支持编写多重继承的代码
  1. 一个子类可以拥有多个父类
  2. 子类拥有父类的成员变量
  3. 子类继承所有父类的成员变量
  4. 子类对象可以当作任意父类对象使用
  • 多重继承的语法规则
class Derived : public BaseA,public BaseB,public BaseC
{
 
};//多重继承的本质与单继承相同
  • 多重继承问题一(对象地址不同)

#include <iostream>
#include <string>
using namespace std;
class BaseA
{
    int ma;
public:
    BaseA(int a)
    {
        ma = a;
    }
    int getma()
    {
        return ma;
    }
};
class BaseB
{
    int mb;
public:
    BaseB(int b)
    {
        mb = b;
    }
    int getmb()
    {
        return mb;
    }
};
//多重继承
class Derived :public BaseA,public BaseB
{
    
public:
    int mc;
    Derived(int a,int b,int c) :BaseA(a),BaseB(b)
    {
        mc = c;
    }
    void print()
    {
        cout << "ma = " << getma() << ", "
                << "mb = "  << getmb() << ", "
             << "mc = "  << mc << endl;
    }
};
int main()
{
    Derived d(1,2,3);
   
    d.print();
    BaseA* pa = &d;
    BaseB* pb = &d;
    cout << "pa->getma() = " << pa->getma() << endl;
    cout << "pb->getmb() = " << pb->getmb() << endl;
    //发现问题:地址是不同的
    cout << "pa = " << &pa << endl;
    cout << "pb = " << &pb << endl;
}
  • 运行结果
ma = 1, mb = 2, mc = 3
pa->getma() = 1
pb->getmb() = 2
pa = 0073FAF4
pb = 0073FAE8
通过程序可知,pa,pb明明指向同一个对象d,但是地址却不同。
通过多重继承得到的对象可能拥有“不同的地址”!!无解决方案
  • 多重继承问题二(可能产生冗余的成员)

 
#include <iostream>
#include <string>
using namespace std;
class People
{
    string name;
    int age;
public:
    People(string _name,int _age)
    {
        name = _name;
        age = _age;
    }
    void print()
    {
        cout << "my name is " << name << endl;
        cout << "my age is " << age << endl;
    }
};
class Teacher : public People
{
public:
    Teacher(string _name,int _age) : People(_name,_age)
    {
    }
};
class Student : public People
{
public:
    Student(string _name, int _age) : People(_name, _age)
    {
    }
};
class Doctor : public Teacher,public Student
{
public:
    Doctor(string name, int age) : Teacher(name, age), Student(name, age)
    {
    }
};
int main()
{
    Doctor d("chenge",22);
    d.print();
}
  • 运行结果
    Doctor继承与Teacher,Student,而Teacher和studebt都继承与People,这也就导致编译器在编译时不知道选择哪一个print函数,到底是Teacher中的,还是Student中的呢?当多重继承关系闭合时,将产生数据冗余的问题!!!
    解决方法:虚继承
class People{};
class Teacher : virtual public People{};
class Student : virtual public People{};
class Doctor : public Teacher,public Student{};
  • 虚继承能够解决数据冗余问题
  • 中间层父类不再关心顶层父类的初始化
  • 最终子类必须直接调用顶层父类的构造函数
#include <iostream>
#include <string>
using namespace std;
class People
{
    string name;
    int age;
public:
    People(string _name,int _age)
    {
        name = _name;
        age = _age;
    }
    void print()
    {
        cout << "my name is " << name << endl;
        cout << "my age is " << age << 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)
    {
    }
};
class Doctor : public Teacher,public Student
{
public:
    Doctor(string name, int age) : Teacher(name, age), Student(name, age),People(name,age)
    {
    }
};
int main()
{
    Doctor d("chenge",22);
    d.print();
}
  • 运行结果
  • 多重继承问题三(可能产生多个虚函数表)

#include <iostream>
#include <string>
using namespace std;
class BaseA
{
public:
    virtual void printa()
    {
    }
};
class BaseB
{
public:
    virtual void printb()
    {
    }
};
class Derived :public BaseA,public BaseB
{
};
int main()
{
    Derived d;
    cout << "sizeof(d)="<< sizeof(d) << endl;
}
  • 运行结果
因为我在ubuntu64位上运行的,所以一个虚函数表地址占8个字节
#include <iostream>
#include <string>
using namespace std;
class BaseA
{
public:
    virtual void printa()
    {
        cout << "BaseA::printa()" << endl;
    }
};
class BaseB
{
public:
    virtual void printb()
    {
        cout << "BaseB::printb()" << endl;
    }
};
class Derived :public BaseA,public BaseB
{
};
int main()
{
    Derived d;
    cout << "sizeof(d)="<< sizeof(d) << endl;
    BaseA* pa = &d;
    BaseB* pb = &d;
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    pa->printa();
    pb->printb();
    BaseB* pbe = (BaseB*)pa;
    pbe->printb();
    BaseB* pbc = dynamic_cast<BaseB*>(pa);
    pbc->printb();
}
  • 运行结果(在vs2019上测试的)
    可以发现,如果使用BaseB*对指针pa使用普通的类型转换方式进行强制类型,输出结果并不是我们所期望的,在需要进行强制类型转换时,C++中推荐使用新式类型转换关键字。解决方案:dynamic_cast
  • 工程开发中的“多重继承”方式:单继承某个类+实现(多个)接口
 
#include <iostream>
#include <string>
using namespace std;
class Base
{
protected:
    int mi;
public:
    Base(int i)
    {
        mi = i;
    }
    int getI()
    {
        return mi;
    }
    bool equal(Base* obj)
    {
        return (this == obj);
    }
};
class Interface1
{
public:
    virtual void add(int i) = 0;
    virtual void minus(int i) = 0;
};
class Interface2
{
public:
    virtual void multiply(int i) = 0;
    virtual void divide(int i) = 0;
};
class Derived : public Base, public Interface1, public Interface2
{
public:
    Derived(int i) : Base(i)
    {
    }
    void add(int i)
    {
        mi += i;
    }
    void minus(int i)
    {
        mi -= i;
    }
    void multiply(int i)
    {
        mi *= i;
    }
    void divide(int i)
    {
        if (i != 0)
        {
            mi /= i;
        }
    }
};
int main()
{
    Derived d(100);
    Derived* p = &d;
    Interface1* pInt1 = &d;
    Interface2* pInt2 = &d;
    cout << "p->getI() = " << p->getI() << endl;    // 100
    pInt1->add(10);
    pInt2->divide(11);
    pInt1->minus(5);
    pInt2->multiply(8);
    cout << "p->getI() = " << p->getI() << endl;    // 40
    cout << endl;
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
    cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
    return 0;
}
  • 运行结果
  • 一些有用的建议
  1. 先继承自一个父类,然后实现多个接口
  2. 父类中提供equal()成员函数
  3. equal()成员函数用于判断指针是否指向当前对象
  4. 与多重继承相关的强制类型转换用dynamic_cast完成
  • 小结:
  1. C++支持多重继承的编程方式
  2. 多重继承容易带来问题:可能出现“同一个对象的地址不同”的情况,虚继承可以解决数据冗余问题,虚继承使得架构设计可能出现问题
  3. 多继承中可能出现多个虚函数表指针
  4. 与多重继承相关的强制类型转换用dynamic_cast完成
  5. 工程开发中采用“单继承多接口”的方式使用多继承
  6. 父类提供成员函数用于判断指针是否指向当前对象
 
 
posted @ 2020-02-03 22:12  认真做个普通人  阅读(320)  评论(0编辑  收藏  举报