【C++】继承

前言

继承是C++中十分重要的一部分内容,主要功能是实现类的复用,可以在原有功能的基础上继续添加新功能,是面向对象编程由简易变为复杂的重要组成。

基本概念

继承的逻辑类似于生活中的房子/车子继承,原来有多少内容,继承之后就有多少,而C++中的继承相当于拷贝了一份父类/基类的内容(但是访问可能会受父类和子类的权限所限制)。父类/基类,子类/派生类,以后在本文统一叫做父类/子类。

继承的定义

在这里插入图片描述

继承关系和访问限定符

父类访问限定符

private访问
protected访问
public访问

子类继承方式

private继承
protected继承
public继承

子类访问权限

在父类访问限定符和子类继承方式中,以权限较小的为子类的访问权限。
访问权限:public > protected > private

  • 例如:父类:protected,子类:private,因为private < protected,所以父类的成员/成员函数将以private的权限被子类访问。

各个权限的访问原则

不使用继承:

  • private 和 protected 的权限一致,均是外部不可访问,类内部可以访问
  • public类的内外都可访问
    使用继承(最终继承结果):
  • private:继承后,子类不可访问
  • protected:继承后,子类中可以访问
  • public:继承后,保留父类的继承方式,即:子类的内外都可访问

总结

  1. struct 创建的类,默认访问权限是public,class的类默认是private
  2. 实际运用中一般使用public继承,访问权限取决于父类,方便拓展

基类和派生类对象赋值转换

在string中,相同类型的变量可以互相转换,那么在有一部分相同(继承父类),有一部分不同(自己拓展)的情况下赋值的规则又是什么?

子类对象,可以赋值给父类的对象/引用/指针,但是子类自己创建的成员和函数不会赋值给父类的对象,只让父类的那部分内容赋值,这样的操作也叫做"切片"。

![[Pasted image 20240831125228.png]]

  • 如果子类对象赋值给父类指针,那么该指针会指向父类的那部分
  • 如果子类对象赋值给父类引用,那么则会引用父类的那部分

只能子类赋值给父类,因为可以切片,但是父类不能父类不能赋值给子类,因为子类自己的成员和函数不会被创建。

继承中的作用域

继承后,子类部分和父类部分的作用域不同,所以在子类和父类中,可以存在相同名字的成员和函数。

子类中访问变量的规则

  1. 如果你要访问子类,直接访问即可。
  2. 如果要访问父类有两种情况:
    • 编译器先去子类作用域找成员/函数,其次到父类找,再到全局域去找。
    1. 也就是说,如果要用的父类变量/函数且子类没有,编译器会先找子类,找不到之后再到父类。
    2. 如果要用的父类变量/函数子类中也有,那么编译器会优先用子类的,这时,我们就必须用到范围解析运算符也就是直接去父类的域中查找:Father::_name
  3. 隐藏:因此,也可以得出,子类与父类中,相同名字的函数并不构成重载(尽管父类的那部分已经被子类继承),因为不在同一作用域,他们构成的关系是隐藏。
    • 成员函数名相同就构成隐藏
class Father
{
protected:
	string _name;
	int _i;
};

class Child : public Father
{
public:
	string _name;

	// 打印父类和子类的成员
	Func()
	{
		cout << _name << endl;  // 由于子类有_name,直接用
		cout << _i << endl;     // 子类中找不到_i,去父类找,打印父类的
		cout << Father::_name << endl;  // 直接指定访问父类的_name,跳过子类
	}
};

int main()
{
	Child ch;
	ch.Func();

	return 0;
}

继承中的默认成员函数

子类初始化

1.构造函数初始化

子类的初始化包括了两个过程:

  1. 父类部分用构造函数的初始化
    • 如果父类没有默认构造函数,需要显式调用父类的构造函数
      • 调用方式:在子类的初始化列表中显式调用
    • 父类的构造函数必须最先调用(父类的成员必须第一个初始化)
      • 因为编译器按照声明顺序初始化,而父类的声明顺序默认排在子类前面,所以一般都是父类的优先初始化
  2. 子类部分用构造函数的初始化
    • 子类初始化和一般的类初始化一样,正常初始化就好

2.拷贝构造函数初始化

同上,子类需要用到拷贝构造函数时,先调用父类的拷贝构造函数,拷贝父类部分,再调用子类拷贝构造函数

3.operator=

先调用父类的operator=进行父类部分的赋值,再调用子类的operator=

4.析构函数的调用

析构函数系统会自动调用,先调用子类的,再调用父类的,顺序和构造函数正好相反,类似于栈的后进先出,先构造的最后析构。不用显式掉用

5.继承与友元

规定:父类中的友元函数并不会继承到子类中,如果需要用到该函数,子类需要手动添加友元函数

class Father
{
protected:
	friend void Add(int* a, int* b);
};

class Child : public Father
{
	// 子类需要手动写出友元函数
	friend void Add(int* a, int* b);
};

6.继承与静态成员

静态成员继承时不会再进行创建,所有子类与父类共用一个静态成员

class Father
{
	static int a;
};

// 静态成员变量定义 
int Father::a = 0;

class Child : Father
{
	
};

int main()
{
	// 把a赋值为1
	Father::a = 1;
	// 由于共用同一个静态成员,所以这里是把a从1改为3
	Child::a = 3;

	return 0;
}
  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值