【C++课程学习】:继承:默认成员函数

embedded/2024/11/14 22:29:05/

🎁个人主页:我们的五年

🔍系列专栏:C++课程学习

🎉欢迎大家点赞👍评论📝收藏⭐文章

 

目录

构造函数

🍩默认构造函数(这里指的是编译器生成的构造函数):

🍩显式写构造函数: 

拷贝构造函数:

🍩编译器自己生成的拷贝构造:

🍩显式写拷贝构造函数:

赋值重载:

析构函数:

🍩析构函数名称变化

🍩父类和子类的析构顺序

继承和友元:

继承和静态成员:

菱形继承:

继承的总结和反思:


构造函数

子类的构造函数在初始化列表时,我们发现父类的声明在子类之前。所以不管在初始化列表怎么写,都是先初始化父类,再调子类的构造。

🍩默认构造函数(这里指的是编译器生成的构造函数):

首先来看看,如果我们在子类中不显式写构造函数,看看编译器生成的默认构造会干什么事情(环境:VS2022):

我们可以把子类的成员分成三类:

1.父类的成员。(看成整体)

2.子类的内置类型。

3.子类的自定义类型。

从VS2022中可以看出,如果不写字类的默认构造函数,那么编译器生成的默认构造函数做了:

1.调用父类的构造函数,对父类成员进行初始化。

2.内置类型不做处理。(有些环境可能会初始化为0)。

3.子类的自定义类型,调用它的构造函数。(有缺省值,进行有参的构造函数)。


🍩显式写构造函数: 

我们在显示写构造函数时,一定要去调用父类的构造函数,构造函数调用的规则如下:
想调用哪个构造函数就传什么参数,根据不同的参数,可以调用不同的构造函数。

class person {    

public:
    person()        //无参的构造函数
    {
        cout << "person()" << endl;
    }
    person(int age,string name)        //传age,和name的构造函数
        :_age(age)
        ,_name(name)
    {
        cout << "person(int age,string name)" << endl;
    }
protected:
    int _age;
    string _name;
};

	student(int num):person()    //父类构造函数的调用,_num(num){}


当我们显式写子类的构造函数时,但是又没有调用父类的构造函数,编译器会帮我们怎么处理?

如果我们没有调用构造函数,那么编译器会去调用父类默认构造函数(无参的构造函数)。


但是会有一个问题?,如果父类没有无参的构造函数怎么办?

此时,由于子类写构造函数时,没有调用父类的构造函数。让编译器去调用父类的无参的构造函数。结果父类还没有无参的构造函数,编译器就会报错。

解决办法就是:在子类构造函数的初始化列表中调用父类带参的构造函数。

全缺省的可以不传参,所以也可以调到。

另外写person()=default;

可以让编译器强制生成默认构造函数。

拷贝构造函数:

🍩编译器自己生成的拷贝构造:

1.对于子定义类型,调用它的拷贝构造。

2.对于内置类型,进行值拷贝。

3.对于父类,调用父类的拷贝构造。

🍩显式写拷贝构造函数:

当我们显式写拷贝构造时,我们就需要注意要去显示调用父类的拷贝构造。

当我们显式写了拷贝构造,却又没有在初始化列表调用父类的拷贝构造,编译器不会帮我们调用父类的拷贝构造,因为父类的拷贝构造是带参的。

像下面一样,如果我们要显式调用person的拷贝构造,我们要怎么给person的拷贝构造传参呢?

传过来的是student类型的对象st,怎么变成person呢?

父类和子类有这样的特点:(赋值兼容转换

子类对象可以赋值给父类的对象,父类的指针,父类的引用。

解决办法:直接传student类型的对象st就可以了。

    //父类拷贝构造person(const person& p):_age(p._age),_name(p._name){cout << "person(person & p)" << endl;}//子类拷贝构造student(const student& st):person(st),_num(st._num){}

赋值重载:

先来看看下面这种写法的赋值运算符重载可行不?

答案是不行的,此时父类的operator=和子类operator=构成隐藏,它会去调用子类的operator=。

然后就会进入死循环

解决办法:在operator=前面指定父类的作用域。

person::operator=(st);

	//父类(基类)的赋值重载person& operator=(person& p){if (&p != this){_age = p._age;_name = p._name;}return *this;}//子类(派生类)的赋值重载student& operator=(student& st){if (this != &st){operator=(st);_num = st._num;}return *this;}

析构函数:

🍩析构函数名称变化

由于析构函数的名称最后都会被处理成destruct,所以父类的析构和子类的析构是构成隐藏关系的。要想调用父类的析构函数,必须指定在父类的作用域。

🍩父类和子类的析构顺序

必须保证先析构子类,再析构父类。

由于在构造函数中,初始化列表是按照声明的顺序进行,父类的声明在子类的前面,所以可以保证父类先初始化,子类后初始化。所以构造函数可以只调用子类构造函数。

但是在析构中,编译器会在子类对象声明周期结束时,先调用子类析构,然后再调用父类析构。

	~person(){cout << "~person()" << endl;}	~student(){cout << "~student()" << endl;}

继承和友元:

友元关系不能继承,父类的友元不能访问子类的私有成员。父亲的朋友不一定是我的朋友。

继承和静态成员:

如果父类定义了静态成员,那么在整个继承体系中,就只有这样一个成员,不管派生出多少个子类,都只有一个。

菱形继承:

单继承:一个子类只有一个直接父类时称这个继承关系为单继承。

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。

菱形继承 菱形继承是多继承的一种特殊情况。

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。
在Assistant的对象中Person成员会有两份。
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和
Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地
方去使用。

继承的总结和反思:

1.在实际中,一定不要设计出菱形继承。

2.类复用的方式有继承组合两冲方式。

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。

组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

优先使用组合,再是继承。

3.继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。


4.对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。


5.实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。


http://www.ppmy.cn/embedded/137606.html

相关文章

【Qt-ROS开发】使用 Qt Creator 构建和编译含 ROS 库的 Qt 项目

【Qt-ROS】使用 Qt Creator 构建和编译含 ROS 库的项目 网上大多数办法是在 Qt creator中安装 ros_qtc_plugin 插件&#xff0c;项目以 ROS1 工作空间的形式构建&#xff0c;还是使用 catkin 来构建整个项目。但是这种方式局限很大&#xff0c;导入 Qt 的组件反而变得很麻烦&a…

机器情绪及抑郁症算法

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年11月12日17点02分 点击开启你的论文编程之旅https://www.aspiringcode.com/content?id17230869054974 计算机来理解你的情绪&a…

python+pptx:(三)添加统计图、删除指定页

目录 统计图 删除PPT页 from pptx import Presentation from pptx.util import Cm, Inches, Mm, Pt from pptx.dml.color import RGBColor from pptx.chart.data import ChartData from pptx.enum.chart import XL_CHART_TYPE, XL_LABEL_POSITION, XL_DATA_LABEL_POSITIONfil…

软硬互联——革新机器人非标产线智能制造

在智能制造的浪潮中&#xff0c;每一条生产线都承载着企业向智能化、高效化转型的梦想。特别是对于机器人非标产线而言&#xff0c;如何在复杂多变的生产任务中保持高效、灵活与智能化&#xff0c;成为了众多制造企业关注的焦点。今天&#xff0c;就让我们一同探索IO模块、网关…

【国产MCU系列】-GD32F4内存映射

了解GD32F4内存映射 文章目录 了解GD32F4内存映射1、基于ARM的处理器介绍2、Cortex和基于Cortex-M的处理器3、Cortex-M的核心寄存器4、Cortex-M的内存映射5、GD32F4xx的内存映射1、基于ARM的处理器介绍 ARM处理器是指由多家硅制造商生产的CPU的基本构建块(因此称为核心)的多…

DDE(深度桌面环境) Qt 6.8 适配说明

内容来源&#xff1a;deepin Plant Qt 6.8 发布已经有一段时间了&#xff0c;各个发行版尝试移植 DDE 时发现包括 dde-shell 在内的几个组件存在比较明显的问题&#xff0c;DDE 小组进行了相关的紧急修复。由于 DDE 部分项目也在分叉维护的状态&#xff0c;为了方便各位移植人…

Three.js中文教程

⬇️ 整理了一些 Three.js 的笔记&#xff0c;有兴趣的工友可以看看 ⬇️ 《Three.js中文教程》

Go 语言已立足主流,编程语言排行榜24 年 11 月

Go语言概述 Go语言&#xff0c;简称Golang&#xff0c;是由Google的Robert Griesemer、Rob Pike和Ken Thompson在2007年设计&#xff0c;并于2009年11月正式宣布推出的静态类型、编译型开源编程语言。Go语言以其提高编程效率、软件构建速度和运行时性能的设计目标&#xff0c;…