C++类的构造与析构特点及作用详解
一、类的构造函数
什么是构造函数
和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数
概念模糊、直接举例:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
构造函数的特点
直接先来说特点吧,然后论证:
1、构造函数在定义对象的时候被调用
2、构造函数可以进行函数重载,可以有很多个
3、创建对象时默认调用的是无参构造
证明1:
构造函数在定义对象的时候被调用;
论证如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
我们在Test()构造的输出语句上加断点、当程序调用Test的printf肯定会停下来,这个时候我们转到反汇编,单步步过、直到函数返回之后,就能知到刚刚是在哪里调用的构造函数了
vs2010:F7编译、F5调试、ALT+8反汇编:
F10一直运行到返回:
这里编译器做了优化,可以直接看出来是在Test定义对象的时候调用了构造。
证明2:
构造函数可以进行函数重载,可以有很多个;
论证如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
重载了两个,注意:调用有参数的构造时,需要传递参数。
运行可以通过:
证明3:
创建对象时默认调用的是无参构造;
论证如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
首先我们删除无参构造,看看能否编译通过:
不可以
然后删除有参构造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
可以
全部都加上:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
运行结果:
这已经证明了,Test te;平常这样定义对象的时候,调用的是无参构造。如果需要调用有参构造,必须传入参数;
这个特点很简单、有参函数肯定要传参嘛,所以定义对象的时候肯定要传入参数啊;
但是这里建议无论什么时候写类,最好还是写上无参构造,哪怕什么都不做也尽量写上,避免不必要的麻烦。
构造函数的作用
一般用于初始化类的成员
如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
运行如下:
初始化成功。
二、类的析构函数
什么是析构函数
类的构造函数名前加上'~'这个符号,就是类的析构函数
概念模糊、代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
~Test(){}就这个样子
析构函数的特点
依然直接先来说特点,然后论证:
1、析构函数不能重载、不能有参数
2、析构函数在变量声明周期结束时被调用
3、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象
证明1:
析构函数不能重载、不能有参数;
编译不通过。
既然不能有参数,那重载更不可能了
证明成功。
证明2:
析构函数在变量声明周期结束时被调用;
局部变量的生命周期是在一个大括号内,即一个所处块结束。
所以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
运行结果如下:
断点
结果
证明成功。
证明3:
析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象;
已知堆栈中定义的对象(局部变量)在块语句结束之后就会被调用,那么带有return的main函数是在返回前调用析构,还是返回后呢?
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
断点-调试-汇编:
可以看到是在函数返回前被调用的。
如果在全局区定义的对象呢?
这个问题很难说,像我一样定义两个断点就行了,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
运行:
发现一运行就断在了return,当我们发F10继续运行的时候,并没有直接调用析构,而是到了括号那里:
继续F10:
通过翻译,可以得知这就是进程结束时的一些收尾工作。
继续F10:
现在大概可以总结了,类的对象定义为全局变量时,是在main函数结束之后进程退出之前,调用的析构函数。
小结
当类的对象定义为局部变量时(堆栈),定义这个对象的块作用域结束时就会调用该对象的析构函数,如果在main函数这个块作用域中定义的对象,那么就是在return之前调用析构。
当类的对象定义为全局变量时(全局区),会在main函数return函数返回之后和进程结束之前,调用该对象的析构函数。
析构函数的作用
我们知道了析构函数都是在类的对象生命周期结束时被调用,那么就代表下面不会再使用到这个对象;所以析构函数一般用于一些收尾的工作,以防忘记。
比如当你使用了该对象的成员申请了内存(malloc、new等)、或者open了一些文件,那么可以在析构函数中free delete 或者close。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
复制讲解
这里我就不运行了,大家可以自己测试下。
总结
构造函数
1、和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数
2、构造函数在定义对象的时候被调用
3、构造函数可以进行函数重载,可以有很多个
4、创建对象时默认调用的是无参构造
析构函数
1、类的构造函数名前加上'~'这个符号,就是类的析构函数
2、析构函数不能重载、不能有参数
3、析构函数在变量声明周期结束时被调用
4、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象
