知识点2【多继承】

多继承:一个子类 继承多个父类。

class 子类名:继承方式1  父类1,继承方式2 父类2,....
{
    //子类新增的数据
}

1、子类和父类的构造与析构顺序

构造顺序:先父类 后子类,多个父类的构造顺序 由子类继承的顺序决定。 析构顺序相反。

#include <iostream>
using namespace std;
class Base1
{
public:
    int a;
public:
    Base1()
    {
        a=0;
        cout<<"Base1的无参构造 a="<<a<<endl;
    }
    Base1(int a)
    {
        this->a=a;
        cout<<"Base1的有参构造 a="<<a<<endl;
    }
    ~Base1()
    {
        cout<<"Base1的析构函数 a="<<a<<endl;
    }
};
class Base2
{
public:
    int a;
public:
    Base2()
    {
        a=0;
        cout<<"Base2的无参构造 a="<<a<<endl;
    }
    Base2(int a)
    {
        this->a=a;
        cout<<"Base2的有参构造 a="<<a<<endl;
    }
    ~Base2()
    {
        cout<<"Base2的析构函数 a="<<a<<endl;
    }
};

class Son:public Base1, public Base2
{
public:
    int a;
public:
    Son()
    {
        a=0;
        cout<<"Son的无参构造 a="<<a<<endl;
    }
    Son(int a,int b, int c):Base1(a),Base2(b)
    {
        this->a=c;
        cout<<"Son的有参构造 a="<<this->a<<endl;
    }
    ~Son()
    {
        cout<<"Son的析构函数 a="<<a<<endl;
    }
};

int main(int argc, char *argv[])
{
   Son ob1(10,20,30);
    return 0;
}

多继承&菱形继承&虚继承_多继承

2、子类和父类的同名处理(加父类作用域)

子类和父类的数据成员同名,子类优先使用自身数据,如果调各自父类的同名成员 加父类的作用域。

子类和父类的函数成员同名,子类的函数 会屏蔽 任何父类的所有同名函数。(加父类的作用域)

#include <iostream>
using namespace std;
class Base1
{
public:
    int a;
public:
    Base1()
    {
        a=0;
        cout<<"Base1的无参构造 a="<<a<<endl;
    }
    Base1(int a)
    {
        this->a=a;
        cout<<"Base1的有参构造 a="<<a<<endl;
    }
    ~Base1()
    {
        cout<<"Base1的析构函数 a="<<a<<endl;
    }
    void func(void)
    {
        cout<<"Base1 func void"<<endl;
    }
};
class Base2
{
public:
    int a;
public:
    Base2()
    {
        a=0;
        cout<<"Base2的无参构造 a="<<a<<endl;
    }
    Base2(int a)
    {
        this->a=a;
        cout<<"Base2的有参构造 a="<<a<<endl;
    }
    ~Base2()
    {
        cout<<"Base2的析构函数 a="<<a<<endl;
    }
    void func(void)
    {
        cout<<"Base2 func void"<<endl;
    }
    void func(int)
    {
        cout<<"Base2 func int"<<endl;
    }
};

class Son:public Base1, public Base2
{
public:
    int a;
public:
    Son()
    {
        a=0;
        cout<<"Son的无参构造 a="<<a<<endl;
    }
    Son(int a,int b, int c):Base1(a),Base2(b)
    {
        this->a=c;
        cout<<"Son的有参构造 a="<<this->a<<endl;
    }
    ~Son()
    {
        cout<<"Son的析构函数 a="<<a<<endl;
    }
    void func(void)
    {
        cout<<"Son func void"<<endl;
    }
};
int main(int argc, char *argv[])
{
   Son ob1(10,20,30);
   cout<<ob1.a<<endl;//30
   cout<<ob1.Base1::a<<endl;//10
   cout<<ob1.Base2::a<<endl;//20
   ob1.func();
   ob1.Base1::func();
   ob1.Base2::func();
   ob1.Base2::func(10);
    return 0;
}

知识点3【菱形继承】

菱形继承:有公共祖先的继承,叫菱形继承。

多继承&菱形继承&虚继承_虚继承_02

#include <iostream>
using namespace std;
class Animal
{
public:
    int a;
};
class Sheep:public Animal{};
class Tuo:public Animal{};
class SheepTuo:public Sheep,public Tuo{};
int main(int argc, char *argv[])
{
   SheepTuo ob1;
   cout<<ob1.Sheep::a<<endl;
   cout<<ob1.Tuo::a<<endl;
    return 0;
}

知识点4【虚继承】

继承的时候 需要加上关键字virtual。能保证子类只会拥有一份公共祖先的数据。

多继承&菱形继承&虚继承_父类_03

多继承&菱形继承&虚继承_子类_04

多继承&菱形继承&虚继承_菱形继承_05

cl /d1 reportSingleClassLayoutAnimal main.cpp

1、菱形继承的内存布局

Animal类的布局:

多继承&菱形继承&虚继承_菱形继承_06

Sheep类的布局:

多继承&菱形继承&虚继承_多继承_07

Tuo类的布局:

多继承&菱形继承&虚继承_虚继承_08

SheepTuo类的布局:

多继承&菱形继承&虚继承_虚继承_09

2、虚继承的内存布局

多继承&菱形继承&虚继承_父类_10

多继承&菱形继承&虚继承_子类_11

#include <iostream>
using namespace std;
class Animal
{
public:
    int a;
};
class Sheep :virtual public Animal {};
class Tuo :virtual public Animal {};
class SheepTuo :public Sheep, public Tuo {};

int main(int argc, char* argv[])
{
    SheepTuo ob1;
    ob1.a = 100;
    
    int off_set = *((int*)(*(int*)(&ob1)) + 1);
    cout << off_set << endl;

    cout << *(int*)((char*)(&ob1) + off_set) << endl;

    return 0;
}

多继承&菱形继承&虚继承_虚继承_12

总结:虚继承的原理

如果一个类 virtual继承父类,子类就会产生一个虚基类指针(vbptr)指向虚基类表(vbtable),而vbtable存储的是通过vbptr访问公共数据的偏移量。