C++语言学习(十六)——多继承

本文详细介绍了C++中的多继承,包括多继承的概念、语法规则、内存布局、菱形继承问题及其解决办法——虚继承。还讨论了多继承派生类的对象模型、虚函数表以及指针类型转换的注意事项。最后,提到了多继承的应用示例,并建议在实际编程中谨慎使用多继承。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++语言学习(十六)——多继承

一、多继承简介

1、多继承简介

C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。

2、多继承语法规则

class Derived : public BaseA,
                   public BaseB,
                   public BaseC   
{

};

3、多继承派生类的内存布局

通过多重继承得到的派生类对象可能具有不同的地址。

#include <iostream>

using namespace std;

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

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

class Derived : public BaseA,public BaseB
{
public:
    Derived(int a, int b, int c):BaseA(a),BaseB(b)
    {
        mc = c;
    }
private:
    int mc;
};

struct Test
{
    int a;
    int b;
    int c;
};

int main(int argc, char *argv[])
{
    Derived d(1,2,3);
    cout << sizeof(d) << endl;//12
    Test* p = (Test*)&d;
    cout << p->a << endl;//1
    cout << p->b << endl;//2
    cout << p->c << endl;//3
    cout << &p->a << endl;//1
    cout << &p->b << endl;//2
    cout << &p->c << endl;//3

    BaseA* pa = &d;
    BaseB* pb = &d;
    //子类对象的地址、首位继承类的成员地址
    cout << &d << endl;
    cout << pa << endl;
    cout << &p->a <<endl;
    //子类对象的地址、次位继承类的成员地址
    cout << pb << endl;
    cout << &p->b << endl;

    return 0;
}

上述代码中,Derived类对象的内存布局如下:
C++语言学习(十六)——多继承
Derived类对象从基类继承而来的处成员变量将根据继承的声明顺序进行依次排布。基于赋值兼容原则,如果BaseA类型指针pa、BaseB类型指针pb都指向子类对象d,pa将得到BaseA基类成员变量ma的地址,即子类对象的地址;pb将得到BaseB类成员变量mb的地址;因此,pa与pb的地址不相同。

4、菱形多继承导致的成员冗余

C++语言学习(十六)——多继承
上述类图中,Teacher类和Student类都会继承People的成员,Doctor会继承Teacher类和Student类的成员,因此Doctor将会有两份继承自顶层父类People的成员。

#include <iostream>
#include <string>

using namespace std;

class People
{
public:
    People(string name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void print()
    {
        cout << "name: " << m_name
             << " age: " << m_age <<endl;
    }
private:
    string m_name;
    int m_age;
};

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 + "_1", age),
        Student(name + "_2", age)
    {

    }
};

int main(int argc, char *argv[])
{
    Doctor doc("Bauer", 30);
    //doc.print();//error
    //error: request for member 'print' is ambiguous
    //Doctor继承了从Teacher,Student继承来的print函数。

    //doc.People::print();//error
    //error: 'People' is an ambiguous base of 'Doctor'
    //People被继承了两次

    doc.Teacher::print();//name:bauer_1 age:30
    doc.Student::print();//name:bauer_2 age:30

    return 0;
}

二、虚继承

1、虚继承简介

在多继承中,保存共同基类的多份同名成员,可以在不同的数据成员中分别存放不同的数据,但保留多份数据成员的拷贝,不仅占有较多的存储空间,增加了成员的冗余,还增加了访问的困难。C++提供了虚基类和虚继承机制,实现了在多继承中只保留一份共同成员。
C++对于菱形多继承导致的成员冗余问题的解决方案是使用虚继承。
虚继承中,中间层父类不再关注顶层父类的初始化,最终子类必须直接调用顶层父类的构造函数。
虚继承的语法如下:
class 派生类名:virtual 继承方式 基类名

2、虚继承示例

#include <iostream>
#include <string>

using namespace std;

class People
{
public:
    People(string name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void print()
    {
        cout << "name: " << m_name
             << " age: " << m_age <<endl;
    }
private:
    string m_name;
    int m_age;
};

class Teacher : virtual public People
{
public:
    Teacher(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值