【C++】继承

本文深入探讨了C++中的继承概念,包括继承的定义、赋值兼容性、作用域规则以及派生类的默认成员函数。详细解释了如何处理同名变量、继承与友元的关系、静态成员的特性,并着重阐述了菱形继承带来的问题及其解决方案——虚继承。通过实例展示了如何利用虚继承解决二义性问题,确保正确访问基类成员。
摘要由CSDN通过智能技术生成

一、继承的概念

1. 继承的定义

保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类

image-20220416131815834


继承规则(私密程度:private > protected > public)

image-20220416155619652


2. 继承的赋值兼容

切割、切片:

  • 子类的值,地址,引用都可以赋值给父类

这里不是类型转换,而是语法支持,中间不会产生临时变量

  • 但父类不能给子类,父类的指针和引用可以给子类,但注意可能存在越界问题,因为子类把父类看作一个子类的

3. 继承的作用域

  • 在继承体系中基类和派生类都有独立的作用域

    当遇到同名变量时遵从就近原则(全局和局部变量的话,优先访问局部,想要访问全局加上::)

    在此假设上,如果子类和父类存在同名变量,当调用子类成员函数访问时,优先访问子类变量

    这种现象叫隐藏/重定义

  • 尽管继承了,如果想要访问父类成员必须指定作用域


4. 派生类的默认成员函数

  • 基类成员调用基类成员的默认成员函数
  • 子类的内置类型成员,调用自己的默认成员函数,自定义类型成员,调用自定义类型的成员函数

  • 父类没有默认成员函数,必须自己写

  • 如果存在浅拷贝和资源释放的问题,系统的默认成员函数不能支持,必须自己写

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class parent
{
public:
	parent(const char* name = "peter")
		: _name(name)
	{
		cout << "parent()" << endl;
	}

	parent(const parent& p)
		: _name(p._name)
	{
		cout << "parent(const parent& p)" << endl;
	}

	parent& operator=(const parent& p)
	{
		cout << "parent operator=(const parent& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

	~parent()
	{
		cout << "~parent()" << endl;
	}


protected:
	string _name;

};


class child :public parent
{
public:
	//构造函数
	child(const char* s = "hello", int num = 0)
		:parent(s),
		_num(num)
	{}

	//拷贝构造
	child(const child& s)
		:parent(s),
		_num(s._num)
	{}

	//赋值重载
	child& operator=(const child& s)
	{
		if (this != &s)
		{
			parent::operator=(s);
			_num = s._num;
		}
		return *this;
	}

	void print()
	{
		cout << _name << endl;
		cout << _num << endl;

	}
private:
	int _num;
};


int main()
{
	child b = {"ball", 20};
	child c;
	c = b;
	c.print();

	return 0;
}

image-20220417143313485

利用父类的构造函数完成子类的拷贝构造

原理:切片/切割(子类赋值给父类)

  • 关于析构函数

析构函数名字会被统一处理成destructor()
那么子类的析构函数跟父类的析构函数就构成隐藏

子类析构函数结束会自动调用父类析构函数

遵循先析构子类,再析构父类


5. 继承和友元

  • 友元关系不能继承

6. 继承和静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类(继承多少次),都只有一 个static成员实例


7. 菱形继承和菱形虚拟继承

  • **单继承:**一个子类只有一个直接父类

  • **多继承:**一个子类有两个或以上直接父类

  • **菱形继承:**A类,分别先单继承到2个子类B和C,再多继承到一个D类

此时存在问题:

  • 二义性,D类继承父类B和C中都有A类的成员,继承的是哪一个出现问题(当访问的时候不知道指的是谁的,需要指明作用域

    不使用virtual 但指明作用域

    指明作用域后会存分别不同的两份,先继承的放在前面,后继承在后面

    image-20220420162203138

  • 数据冗余,A类有个很大的数组,那D类要继承2份,会冗余


虚继承解决

在菱形的B、C类进行虚继承

class B : virtual public A
{};
class C : virtual public A
{};

此时再访问A成员不会出现二义性的报错(编译器优化成同一个)(在调试窗口会显示)


  • 不使用virtual,指明作用域

内存地址会显示不同

image-20220420162714490

image-20220420162744107

同一个地址的数值发生了变化


  • 当使用virtual,指明作用域时候

image-20220420163501980

这时候会发现,此时,B、C共同继承的A在最后面

image-20220420211353471

A为虚基类

D将其放在公共的地方,B、C可通过访问限定符去获取

如何获取?通过虚基表偏移量

image-20220420214530125

前面存储一个指针可以指向虚基表,而虚基表存储着虚基类的偏移量(存储在哪)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凛音Rinne

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值