为什么会有构造函数和析构函数呢?
1、初始化和销毁经常忘记
2、有些地方写起来很繁琐.
Stack有了构造和析构,就不怕忘记写初始化和清理函数了,也简化了
例如在队列oj时,忘记释放,造成内存泄漏
构造函数
主要任务:初始化对象
我们不写,编译器会自己生成
特征:
- 函数名和类名相同
- 无返回值(也不需要写void)
- 对象实例化时编译器自动调用对应的构造函数
- 构造函数可以重载
构造函数需特殊对待,在调用上也很特殊(如果构造函数不是默认构造函数)
默认构造函数是不需要传参的构造函数(我们没写编译器默认生成的构造函数,全缺省构造函数,无参的构造函数)
那么默认生成的构造函数,处理下面的成员变量仍然是随机值?
它都做了什么?
我们不写,编译器默认生成构造函数,内置类型不做处理,自定义类型会去调用他的默认构造。
有些编译器也会处理但是那是个性化行为不是所有编译器都会处理
C++区分内置类型和自定义类型
1、内置类型/基本类型、语言本身定义的基础类型int/char/double/指针(任意类型的指针)等等
2、自定义、用struct/class等等定义的类型
结论
1、一般情况下,有内置类型成员,就需要自己写构造函数,不能用编译器自己生成的
2、全部都是自定义类型成员,可以考虑让编译器自己生成(编译器会调用自定义类型的构造函数,当然自定义类型的构造函数本身还是需要我们来写的,在这个前提下,只有自定义类型的类就不需要写构造函数)
按理说默认生成的构造函数应该处理一下,不应该是随机值
C++11 给的补丁
在声明成员变量时,给缺省值,给默认构造用,而且这不是初始化,只是声明
析构函数
析构函数:析构函数不是完成对对象本身的销毁,局部对象销毁工作是由
编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作
不写析构,那么类中没有动态申请的资源,对象的成员变量由编译器调用它的析构函数进行释放
即使你写了空的析构,编译器依然会释放成员变量,析构函数只对动态申请的资源进行清理
特性:
- 析构函数名是在类名前面加上字符~
- 无参数,无返回值类型
- 一个类只有一个析构函数,未显示定义,系统自动生成默认的析构函数。并且析构函数不可以重载
- 对象声明周期结束,C++编译器自动调用析构函数
默认生成的析构函数做了什么?
1、内置类型成员不做处理
2、 自定义类型会去调用他的析构函数
什么时候写,什么时候不写?
1、一般情况下,有动态申请资源,就需要显示写析构函数释放资源
2、没有动态申请的资源,不需要写析构
3需要释放资源的成员都是自定义类型,不需要写析构(前提:自定义类型都定义好析构函数)
析构函数调用顺序问题
函数入栈,后进先出,则后实例化的对象先析构
拷贝构造函数
用一个已经存在的对象初始化另一个对象–拷贝构造
这和 已经存在的两个对象之间复制拷贝 还不一样 (涉及运算符重载函数再解释)
拷贝构造函数的定义是这么说的
特征:
拷贝构造函数也是特殊的成员函数,其特征如下:
1.拷贝构造函数是构造函数的一个重载形式。
⒉.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
3.若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
先看看拷贝构造会在什么时候发生
1.函数参数类型为类类型对象
C++规定了,必须要调用拷贝构造去完成
2.使用已存在对象创建新对象
红框也会调用1次拷贝构造
并且红框上面,为什么这里拷贝函数只调用一次,不是应该2次吗,函数传值返回的临时空间拷贝一次,自定义类型赋值一次(虽然是赋值,很像调用了赋值运算符重载函数,但是这是用一个初始化好的对象来初始化另一个未初始化的对象,所以仍然是调用拷贝构造),
答:拷贝构造+拷贝构造 ->优化为拷贝构造
3.函数返回值类型为类类型对象
下面再来说说无穷递归调用是如何发生的
⒉.拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用。
拷贝构造函数Date(Date d)以传值方式定义形参,在调用 Date d2(d1)初始化d2时,Date d和d1需要拷贝构造,他们是自定义类型,等价于Date d(d1)来初始化d这个形参,Date d(d1)又会有一个新的形参d需要初始化,如此下去,造成死循环
正确写法,要加上const防止修改已经初始化好的对象数据
拷贝构造为什么形参要加上const ?
默认生成的拷贝构造函数都干了什么?
1、内置类型成员完成值拷贝/浅拷贝。
2、自定义类型成员会调用他的拷贝构造。
3.若未显式定义,编译器会生成默认的拷贝构造函数。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。
日期类直接调用默认的拷贝函数,我们可以不写,就可以完成
但是想stack就需要我们定义拷贝构造函数,而且需要深拷贝,简单的值拷贝,导致st1,st2的值是同一个(数据依次拷贝,top和capacity没事,但a指向的是同一块空间)
造成问题
1.一个数组中的改变会影响另一个
2.析构两次同一块空间,报错
这个例子可以理解祖师爷为什么规定了需要调用拷贝构造(前提是实现了情况下),而不是像C语言 传值拷贝,导致指向同一块空间
一些调试技巧,利用this看实例化对象
再来看看为什么要有引用
上面两种情况,日期类是函数参数类型为类类型对象会调用构造拷贝12字节(类大小)还是传引用都还行,可以接受
此时函数参数为类类型需要拷贝构造,而且是深拷贝代价比较大,不是简单的值拷贝,就不再想用值传递,而是用引用,提高了效率
这种情况也不想传值返回,仍然需要拷贝构造给临时空间返回
减少拷贝的写法
文章来源:https://www.toymoban.com/news/detail-427315.html
不能返回被销毁的局部变量的引用
暂时只能这么写
虽然效率低一点,但是正确文章来源地址https://www.toymoban.com/news/detail-427315.html
到了这里,关于类的默认成员函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!