首页 > 编程语言 >【C++ | 继承】|概念、方式、特性、作用域、6类默认函数

【C++ | 继承】|概念、方式、特性、作用域、6类默认函数

时间:2024-07-02 23:28:09浏览次数:19  
标签:作用域 成员 基类 默认 继承 num C++ 派生类 public

继承

1.继承的概念与定义

继承是面向对象编程中的一个重要概念。它的由来可以追溯到软件开发中的模块化设计代码复用的需求。

在软件开发过程中,我们经常会遇到需要为多个类添加相同的行为或属性的场景,这样就产生了代码重复的问题。为了解决这个问题,工程师们开始寻找一种方法来实现代码的复用。

继承就是一种解决代码复用问题的方式。它允许我们创建一个新的类,继承自一个已经存在的类,从而继承和复用父类的属性和方法。通过继承,我们可以在不改变父类的前提下,为子类添加额外的属性和方法,实现功能的扩展。

继承方式的由来可以追溯到早期的面向对象编程语言。早期的面向对象编程语言如Smalltalk、Simula等提供了基于类的继承机制。后来的编程语言如Java、C++等也引入了类似的继承机制。继承方式的由来和发展是为了提高软件开发的效率和可维护性,同时也体现了面向对象编程的思想和原则。

总而言之,在C++中,继承是代码复用的最重要手段。

2.继承的方式

那么我们要如何实现继承呢?
先看个样例,再看语法规则:

class parent
{
public:
	int _age;
};

class child : public parent
{
public:
	int _id;
};
  • 以上就是一个简单的继承结构,child继承了parent。

  • 其中parent这种被别人继承的类叫做:基类 / 父类

  • child这种继承别人的类叫做:派生类 / 子类

  • public parent这条语句,紧跟在child的类名后面,说明child继承了parent。而public是继承方式

这个继承方式有什么作用呢?

继承方式与基类的成员访问限定符共同决定了派生类对基类成员的访问权限

基类成员 \ 继承方式public继承protect继承private继承
public成员派生类的public成员派生类的protect成员派生类的private成员
protect成员派生类的protect成员派生类的protect成员派生类的private成员
private成员不可见不可见不可见
  • 以上表格展示了所有情况下的继承,基类的成员会根据自身的访问属性以及继承方式,共同决定最终继承到派生类的成员是什么属性。

  • 其中,不可见不是指不继承,基类中的private成员继承后在派生类中不可见,就是派生类无法直接访问到这个成员,但是派生类依然是存储着这个成员的。

当然,与访问限定符一样,继承方式也是有默认方式

  • 用class定义的类,默认继承方式是private
  • 用struct定义的类,默认的继承方式是public

  • 多继承:
    一个派生类可以同时继承多个基类:
class parent1
{
public:
	int _age;
};

class child : public parent1, public parent2
{
public:
	int _id;
};

2.1继承基本特性

  • 继承后,派生类有可能只增改了基类的成员函数,而成员变量是一样的,所以基类和派生类的大小可能是一样的
  • 友元关系不能继承,基类的友元不能访问子类的私有和保护成员
  • 对于基类的静态成员,派生类和基类共用,派生类不会额外创建静态成员
  • 如果不希望一个类被继承,可以将这个类的构造函数或者析构函数用private修饰
    继承后,派生类的初始化列表指向顺序为继承顺序

2.2继承的作用域

基类与派生类有两个分别独立的作用域

2.2.1隐藏

当派生类继承了基类的成员后,如果派生类自己创建了与基类同名的成员,那么派生类成员将屏蔽对同名基类成员的直接访问,这种情况叫做隐藏。

class A
{
	void func()
	{}

public:
	int num;
};

class B : public A
{
	void func()
	{}

public:
	int num;
};

在以上继承关系中,B继承了A的num变量func函数,而B类自己还创建了同名的func与num。那么此时在B内部直接访问num与func,就是访问B自己的num。如果想要访问A的成员,需要限定作用域

B b;
b.func();//访问B的func函数
b.A::func();//访问A的func函数
  • 此外,函数重载要求两个函数在同一个作用域,而基类与派生类是两个不同作用域,所以就算参数不同也不能构成重载。所以只要基类与派生类内的函数名相同就构成隐藏,不考虑参数

赋值兼容

赋值兼容是一个基类与派生类之间的转换规则,其可以让派生类转换为父类。

以如下的继承关系做讲解:

class person
{
public:
	string _name;
	string _sex;
	int _age;
};

class student : public person
{
public:
	int _No;
};

规则

派生类的对象可以赋值给基类的对象

student s;
person p = s;

赋值如下图


在这里插入图片描述


我们可以将一个派生类的成员赋值给基类成员,此时会发生一个切片效果,基类只取出派生类中属于基类的部分来构造基类。

派生类的指针可以转换为基类的指针
派生类的引用可以转换为基类的引用

student s;
person* pp = &s;
person& rp = s;

在这里插入图片描述


基类是被包含在派生类中的,所以我们用基类的指针去访问派生类,相当于只访问了基类的部分。上图中就是只访问了红色的部分。


派生类的创建和销毁

派生类是如何创建销毁的?因为派生类内部还包含了一个基类,那么基类这一部分要如何处理?
其实想要理解这一部分,就记住一句话:派生类的默认成员函数,把基类当作一个类成员变量处理

接下来我为大家讲解构造函数,拷贝构造,赋值重载,析构函数这几个与创建销毁相关的函数,来理解派生类是如何创建销毁的。

构造函数

派生类构造函数将基类当作一个成员变量,不会直接初始化基类的成员,而是通过调用基类的构造函数

在一般的类中,类内部如果有其他类的成员变量,构造函数会在初始化列表调用其构造函数。如果不直接调用,那么会隐式调用其相应的默认构造函数。

class person
{
public:
	string _name;
};

class child : public parent
{
public:
	child(string name, int num)
		:parent(name)
		,_num(num)
	{}
private:
	int _num;
};

:parent(name) 就是在初始化列表显式地调用构造函数。

拷贝构造

派生类拷贝构造将基类当作一个成员变量,不会直接拷贝基类的成员,而是通过调用基类的拷贝构造。

在一般的类中,类内部如果有其他类的成员变量,拷贝构造会在初始化列表调用其拷贝构造。如果不直接调用,那么会隐式调用其相应的默认构造函数。

class person
{
public:
	string _name;
};

class child : public parent
{
public:
	child(const child& c)
		:parent(c)
		,_num(c.num)
	{}
private:
	int _num;
};
  • parent ( c ) 就是在显式调用基类的拷贝构造,不过我们在调用基类的拷贝构造时,传入的却是派生类的引用。这是为什么?❓❔❓
  • 我们刚在赋值兼容处说过:派生类的引用可以转化为基类的引用
  • 所以此处在传参时会发生一次隐式的切片,基类的拷贝构造只访问派生类的基类部分,来拷贝出一个基类。

要注意:拷贝构造也属于构造函数,所以拷贝构造在初始化列表中如果没有显式调用拷贝构造,就会隐式调用默认构造函数


赋值重载

在派生类拷贝构造中,必须显式调用基类的赋值重载,因为赋值重载也把基类当作一个类成员做处理。赋值重载不会直接调用成员的赋值重载,而是需要我们显式调用。

class person
{
public:
	string _name;
};

class child : public parent
{
public:
	child& operator=(const child& c)
	{
		parent::operator=(c);
		_num= c._num;
	}
private:
	int _num;
};

parent::operator=(c );就是在显式地调用基类的拷贝构造,这里不能直接调用operator=(c );,因为派生类中存在operator=;这个函数,基类的函数被隐藏了,所以我们要指定作用域,来调用基类的赋值重载。

标签:作用域,成员,基类,默认,继承,num,C++,派生类,public
From: https://blog.csdn.net/shijiaqingsnfx/article/details/139997010

相关文章

  • C++字体库开发
    建议根据字体需求,多个组合使用。高度定制可基于freeType+harfbuzz基础库完成。GitHub-GNOME/pango:Read-onlymirrorofhttps://gitlab.gnome.org/GNOME/pangoGitHub-googlefonts/fontview:Demoappthatdisplaysfontswithafree/libre/open-sourcetextrenderi......
  • 车站选票代码分析与展示(C++版)
    目录程序的主要功能1.主窗口:2.管理员窗口:3.普通顾客窗口:主要数据结构函数间调用关系算法流程图1.查询算法流程图​编辑2.乘客买票算法流程图程序运行结果1.主窗口->管理员窗口a.管理员窗口->验证窗口b.管理员增加车次信息c.浏览全部车辆信息d.注销车次信息e.车......
  • C++基础(二):C++入门(二)
        上一篇博客我们正式进入C++的学习,这一篇博客我们继续学习C++入门的基础内容,一定要学好入门阶段的内容,这是后续学习C++的基础,方便我们后续更加容易的理解C++。目录一、内联函数1.0产生的原因1.1概念1.2特性1.3面试题二、缺省参数2.1缺省参数的概念2.2......
  • C++与C#创建位图,是否需要区分RGB和BGR模式
    在处理位图时,确实需要区分RGB和BGR模式,因为不同的库和API对颜色通道的排序有不同的约定。具体到C++与C#,这一点也是需要注意的。C++创建位图使用GDI+或WIC(WindowsImagingComponent):当你在C++中使用这些WindowsAPI创建或操作位图时,通常会指定像素格式,比如PixelFormat2......
  • 默认路由的“成神之路“
    以下是默认路由的拓扑:以下是cisco的基本命令行:Router>enableRouter#configureterminalRouter(config)#iproute0.0.0.00.0.0.0<下一跳IP地址或接口>Router(config)#exitRouter#writememory 如果要将默认路由设置为通过网关192.168.1.1转发数据包,则命令应为......
  • C++定义函数指针,回调C#
    C++定义函数指针。typedefint(__stdcall*delegate_func)(inta,intb);暴露接口:int__stdcallCPPcallCSharp(delegate_funcfunc);方法实现:int__stdcallCPPcallCSharp(delegate_funcfunc){returnfunc(1,2);}头文件calculator.h#ifndefLIB_CALCULATOR_H#defin......
  • LeetCode 算法:二叉树展开为链表 c++
    原题链接......
  • A tour of C++ 读书笔记
    第一章:C++只是个编译型语言,需要源文件编译成目标文件,再通过链接各种库到可执行文件1.6常量  const  constexpr这个代表是要在编译的时候估值,性能会有所增加吧2.4联合体(union)  联合体所有的成员都是分配在同一地址上面,所以联合体所占的空间是跟其自身内部成员所......
  • 2024年华为OD机试真题-分披萨-C++-OD统一考试(C卷D卷)
    2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述:“吃货”和“馋嘴”两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数扇形小块。但是粗心服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小......
  • C++那些事 研读...
    constthings1.const常量与#define宏定义常量区别const常量编译时期可以进行安全检查,#define宏定义并没有具体的数据类型,只是字符替换罢了,不能安全检查2.const与指针constchar*a;//指向constchar的指针charconst*a;//指向constchar的指针char*consta;//const......