二、C++面向对象-继承(Inheritance)

概念

	继承是从已有的类创建新类的过程,这使得创建和维护一个应用程序变得更容易,达到了重用代码功能和提高执行时间的效率
	
	继承呈现了面向对象程序设计的层次结构,体现了从简单到复杂的认知过程
	
	在 c++ 类中,如类 B 继承于 类 A,则类 A 叫做 基类或者父类,则类 B 叫做 派生类或者子类

语法

class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,...
{
	<派生类类体>
}

继承方式

公有继承(public继承方式)

  • 基类中所有 public 成员在派生类中为 public 属性;

  • 基类中所有 protected 成员在派生类中为 protected 属性;

  • 基类中所有 private 成员在派生类中不能使用。

    基类中的私有成员,就算被public继承,可不可以直接通过派生类的公有成员函数访问,必须通过基类中的公有或者保护函数接口访问

    #include <iostream>
    
    using namespace std;
    
    class A
    {
    public:
        //基类和派生类中出现同名后,在各自类中的构造函数中初始化
        A() : x(100), a(200)
        {
        }
        //基类私有成员通过公有接口可以访问
        int getInfo()
        {
            cout << a << endl;
        }
        int x;
    private:
        int a;
    };
    
    class B : public A
    {
    public:
        B() : x(0)
        {}
        //公共接口中,可以调用基类的公有函数接口
        void getAInfo()
        {
            //区分基类和派生类的同名成员变量,可以使用作用域
            cout << x << endl;
            cout << A::x << endl;
            getInfo();
        }
        double d;
        char c;
        int x;
    };
    
    int main(int argc, const char *argv[])
    {
        cout << sizeof(A) << endl;
        cout << sizeof(B) << endl;
    
        A a;
        B b;
        //区分基类和派生类的同名成员变量,可以使用作用域
        cout << b.x << endl;
        cout << b.d << endl;
        cout << b.A::x << endl;
        b.getAInfo();
    
        return 0;
    }
    
    

保护继承(protected继承方式)

  • 基类中的所有 public 成员在派生类中为 protected 属性;
  • 基类中的所有 protected 成员在派生类中为 protected 属性;
  • 基类中的所有 private 成员在派生类中不能使用。

私有继承(private继承方式)

  • 基类中的所有 public 成员在派生类中均为 private 属性;
  • 基类中的所有 protected 成员在派生类中均为 private 属性;
  • 基类中的所有 private 成员在派生类中不能使用。
继承方式/基类成员public成员protected成员private成员
public继承publicprotected不可见
protected继承protectedprotected不可见
private继承privateprivate不可见

基类的指针或者引用可以指向派生类,反之不可以

组合

class A
{
public:
	void show()
	{
		cout << "A" << endl;
	}
};

class B
{
public:
	void show()
	{
		cout << "B" << endl;
	}
};

class C : public A
{
public:
	void show()
	{
		cout << "C" << endl;
		A::show(); //继承
		b.show(); //组合
	}
private:
	B b;
};

继承中构造函数的调用顺序

  1. 先调用父类构造函数 – 调用构造函数的顺序跟声明顺序有关
  2. 再调用成员对象构造函数(只跟声明顺序有关)
  3. 最后调用自身构造函数

继承中析构函数的调用顺序

和构造函数相反

多继承与多重继承

多继承:一个派生类有多个基类

多重继承:一个派生类作为其他派生类的基类
在这里插入图片描述

问题:

当D类继承于B类和C类,B类与C类有一个共同的基类时,在创建D类的对象时,A类的构造函数将会调用两次,相当于创建两个A类对象

解决方案:

  1. 避免使用这种方式

  2. 使用虚继承

    class A
    {
    public:
    	A()
    	{
    		cout << "A structor" << endl;
    	}
    	~A()
    	{
    		cout << "A destructor" << endl;
    	}
    };
    
    class B : virtual public A
    {
    public:
    	B()
    	{
    		cout << "B structor" << endl;
    	}
    	~B()
    	{
    		cout << "B destructor" << endl;
    	}
    	int a;
    };
    
    class C : virtual public A
    {
    public:
    	C()
    	{
    		cout << "C structor" << endl;
    	}
    	~C()
    	{
    		cout << "C destructor" << endl;
    	}
    	int a;
    };
    
    class E : virtual public C, virtual public B
    {
    }
    

    使用虚继承后,在虚继承的类中,会有一个虚基表指针vbptr,指向虚基表,虚基表中会存在偏移量,这个量就是表的地址到父类数据地址的距离

    注意点:

    1. 虚基类构造函数调用顺序:基类 --> 中间派生类 --> 最末派生类
    2. 虚基类析构函数调用顺序:最末派生类 --> 中间派生类 --> 基类
    3. 虚基类的初始化:基类对象在最末派生类中初始化
    4. VTT(Virtual Table Table):在多重虚继承中,如果一个派生类从多个路径继承同一个虚基类,那么就可能需要处理不同的虚函数表之间的交叉问题。VTT 表的作用就是为了记录虚函数表的地址,以便在处理多重虚继承时能够正确地选择调用哪个虚函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值