当前位置: 首页 > news >正文

【C++闯关笔记】深究继承

系列文章目录

上一篇:【C++闯关笔记】模板的特化-CSDN博客


文章目录

目录

系列文章目录

文章目录

前言

一、继承是什么?

1.继承的概念

2.继承的定义方法

继承方式与访问限定

二、继承相关知识

1.切割

2.隐藏

3.派生类中的默认成员函数

4.友元与静态成员

三、虚继承

1.菱形继承

2.虚继承

总结



前言

为什么C++要引入继承?看下面两个类。

我们观察到下面的Student类,与Teacher类有很多重复的代码,造成了代码冗余有没有什么方法像模板一样能够使代码可以复⽤呢?——继承应运而出。

class Student 
{
public:void Learn(){……;}
private:int _ID;string _name;char _sex;string _addr;
};class Teachr
{
public:void Teach(){……;}
private:int _ID;string _name;char _sex;string _title; 
};


一、继承是什么?

        继承,是C++⾯向对象设计中使代码可以复⽤的最重要的⼿段之一,它允许我们在保持原有类特性的基础上进⾏扩展,增加方法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。

1.继承的概念

我们将上述的Student类与Teacher类中重复的代码提炼出,设计出一个新的Person类:

class Person
{
private:int _ID;string _name;char _sex;
};

将公共的成员都放到Person类中,Student和teacher都继承Person,这样就可以复⽤这些成员,就不需要重复定义了,这样既整洁又省去了很多⿇烦。

Student类与Teacher类继承于Person:

class Student : public Person
{
public:void Learn(){}
private:int _major;//专业
};class Teachr: public Person
{
public:void Teach(){}
private:string _title; //职称
};

其中Person是基类,也称作⽗类。Student是派生类,也称作⼦类

当用Student 创建对象后,对象中除了本身的 _major外还有从Person中继承的:_ID _name _sex。

换句话说,派生类对象由基类子对象(即从基类继承的成员)和派生类自己的成员组成。

2.继承的定义方法

如上述的Student类继承于Person类,继承的定义方法为:

在子类(派生类)之后加一个 ' : ' 与继承方式,再跟上父类(基类)的类名。

继承语法的核心在于继承的方式。
 

继承方式与访问限定

子类继承父类的方式有三类:public、protected、private,它们分别对应子类对父类的三种访问限定。

访问限定解释

已知父类中的类成员有三种类型:

public:外界可访问;

protected:外界不可访问,子类可访问;

private:外界与子类均不可访问。

继承方式也有三种,那么关于继承基类成员访问方式就有3 * 3 = 9形态,如下图所示。

助记总结

①基类private成员在派⽣类中⽆论以什么方式继承都是不可见的。

②如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

③除去基类的private成员,其他成员在派生类的访问方式 == Min(成员在基类的访问限定符,继承方式),public >protected> private

细节提醒

①即使基类中private成员不可被子类访问,但它们还是被继承到了派⽣类对象中,只是语法上限制派⽣类对象不管在类⾥⾯还是类外⾯都不能去访问它们。

②若子类使用关键字class继承时默认的继承⽅式是private,使⽤struct时默认的继承⽅式是public,不过最好显示的写出继承⽅式。

二、继承相关知识

1.切割

        如果是public继承的派生类,那么它的对象可以赋值给基类的指针/基类的引用/基类对象。这⾥有个形象的说法叫切割:寓意把派⽣类中基类那部分切出来,基类指针或引⽤指向的是派⽣类中切出来的基类那部分。

        但基类对象不能赋值给派⽣类对象。

派生类对象赋值给基类对象过程解析

Person person = student;  // 实际发生的过程:
  • 编译器发现studentStudent类型,但personPerson类型

  • 它知道Student继承自Person,所以student对象中包含Person部分

  • 编译器提取student对象中的Person子对象

  • 然后调用Person类的拷贝构造函数,用提取的Person子对象来初始化person

实际上等价于:

Person person(static_cast<Person>(student));  // 提取基类部分,然后拷贝构造

代码示例

int main()
{Student s ;// 1.派⽣类对象可以赋值给基类的指针/引⽤Person* pp = &s;Person& rp = s;//2.基类对象不能赋值给派生类指针/引用Person p;//这里会报错Student* sp = &p;Student& rp = p;//3.派生类对象赋值给基类对象p = s;
}

2.隐藏

①如果派⽣类和基类中有同名成员,派生类成员将屏蔽基类同名成员,这种情况叫隐藏

②如果是成员函数的隐藏,则只需要函数名相同就构成隐藏。

③在派⽣类成员函数中,可以使⽤基类::基类成员显示访问。

比如Person中有个_ID成员,如果Student在继承Person后再定义一个_ID成员,这就构成了隐藏,如:

class Person
{
public:void func(){}
private:int _ID;
};class Student
{
public:void func(int x){}int getID(){return _ID;}
private:int _ID;
}

上述Student中的_ID隐藏了从Person中继承下来的_ID,当调用getID函数返回的是Student类中的ID;Student中的func函数隐藏了从Person中继承下来的func函数。

3.派生类中的默认成员函数

        默认成员函数,意思是指我们不写,编译器会变我们⾃动⽣成。常用到的包括:默认构造函数、默认拷贝构造函数、默认赋值运算符函数、默认析构函数。

默认构造函数派⽣类的构造函数必须调⽤基类的构造函数初始化基类的那⼀部分成员。如果基类中没有默认的构造函数,则必须在派⽣类构造函数的初始化列表阶段显⽰调⽤基类的构造函数初始化派生类中继承的基类对象。

默认拷贝构造函数派⽣类的拷⻉构造函数必须调⽤基类的拷⻉构造,以完成派生类对象中继承的基类成员的拷⻉初始化。

默认赋值运算符函数:派⽣类的operator=必须要调⽤基类的operator=完成基类的复制。

默认析构函数:派生类的析构函数会在被调用完成后自动调⽤基类的析构函数清理基类成员。

细节注意:

①派⽣类的 operator=隐藏了基类的operator=,所以显⽰调⽤基类的operator=需要指定基类作⽤域。

②派⽣类对象初始化先调⽤基类构造再调派⽣类构造。

③派⽣类对象析构清理会先调⽤派⽣类析构再调基类的析构,以保证派生类对象先清理派⽣类成员再清理基类成员的顺序。

什么时候需要自实现上述默认构造函数?

①当子类没有动态资源管理需求时,通常只需要关注构造函数,编译器会自动生成其他特殊成员函数。

②如果需要自定义析构函数(有动态资源管理需求时)、拷贝构造函数或拷贝赋值运算符中的任何一个,那么这三个通常都需要显示实现。

4.友元与静态成员

友元关系不能继承,也就是说基类友元不能访问派⽣类私有和保护成员 。

基类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员。⽆论派⽣出多少个派⽣类,都 只有⼀个static成员实例。

三、虚继承

1.菱形继承

单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承。

多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承。

什么是菱形继承?

菱形继承:菱形继承是多继承的⼀种特殊情况。如定义一个Doctor(博士类),继承于Student与Teacher类(博士既是学生也可能是助教),但Student与Teacher又同时继承于Person类,这就是菱形继承。

菱形继承有数据冗余和⼆义性的问题,在Doctor实例化的对象中Person成员会有两份。
 

2.虚继承

虚继承的概念

        继承是C++中用于解决多重继承中菱形继承问题的一种机制。在菱形继承结构中,一个类(派生类)从两个类继承,而这两个类又共同继承自同一个基类,这样就会导致派生类中包含两份基类的成员,从而产生二义性。虚继承可以确保在菱形继承中,最终派生类中只包含一份基类子对象。

虚继承语法

        在定派生类时,若意识到形成了菱形继承而产生了数据冗余和二义性时,需要在继承语法前加上virtual关键字。

class Person
{}
class Student : virtual public Person
{};
class Teachr: virtual public Person
{}class Doctor:public Student ,public Teacher
{}

注意:产生的冗余数据属于哪个类,就在那个类的子类继承它时,加上virtual。

尽管有虚继承解决菱形继承问题,但同时也带来了额外的复杂性和性能开销。

多继承被公认为C++的缺陷之⼀,后来的⼀些编程语⾔(如Java)都没有多继承,故在实际编程中尽量不要设计出使用菱形继承的类。


总结

本文主要介绍了C++三驾马车之一的继承机制。

首先,介绍了继承的概念与定义方法,并额外介绍继承方式与访问限定的关系,总结了基类成员在派生类的访问方式 == Min(成员在基类的访问限定符,继承方式),public >protected> private;

之后,介绍了继承相关的知识,如基类与派生类之间的切割问题、隐藏问题、默认成员函数行为以及友元与静态成员;

最后,介绍了单继承、多继承,并着重介绍虚拟继承与菱形继承问题。

http://www.dtcms.com/a/515144.html

相关文章:

  • Python爬虫抓取豆瓣TOP250数据
  • AWS Elemental MediaConvert:视频转码不再难
  • 华为OD最新机试真题-乘坐保密电梯-OD统一考试(C卷)
  • SpringBoot 如何实现零拷贝:深度解析零拷贝技术
  • 问卷调查网站怎么做做百度推广
  • Jenkins 持续集成与部署指南
  • 新书速览|DeepSeek高效数据分析:从数据清洗到行业案例
  • 搜索百科(5):Easysearch — 自主可控的国产分布式搜索引擎
  • 自己建商城型网站做设计的软件
  • # 模型量化(二):基于BERT的量化代码实战
  • 网站没有备案会怎样资源网官网
  • 【C++:继承】面向对象编程精要:C++继承机制深度解析与最佳实践
  • Python访问者模式实战指南:从基础到高级应用
  • 《数组和函数的实践游戏---扫雷游戏(基础版附源码)》
  • 专门做网站的软件是网站着陆页怎么做
  • 南京专业网站制作公司如何申请免费网站空间
  • 【乌班图】远程连接(向日葵/ToDesk)显示成功却无桌面的问题解析与解决
  • 异或的应用
  • c++语法——字符串(10.23讲课)
  • AI大事记13:GPT 与 BERT 的范式之争(上)
  • wordpress安装后查看站点失败网站创建多少年了
  • 文件指针控制函数
  • 【JavaEE初阶】 多线程编程核心:解锁线程创建、方法与状态的创新实践密码
  • JavaEE初阶——HTTP/HTTPS 核心原理:从协议格式到加密传输
  • Linux 内存 get_user_pages_remote 函数
  • 【图像处理】图像滤波
  • CSS 列表详解
  • 建设工程规范下载网站商城网站开发的完整流程
  • 同德县网站建设公司海南网站建设及维护
  • 广西送变电建设公司网站深圳市建设工程造价站官网