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

C++类的构造与析构特点及作用详解

一、类的构造函数

什么是构造函数

和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数

概念模糊、直接举例:

1

2

3

4

5

6

7

8

9

10

11

12

#include <stdio.h>

#include <windows.h>

structTest

{

Test()// 和类具有相同的名、并且没有返回值

{

}

};

intmain()

{

return0;

}

构造函数的特点

直接先来说特点吧,然后论证:

1、构造函数在定义对象的时候被调用

2、构造函数可以进行函数重载,可以有很多个

3、创建对象时默认调用的是无参构造

证明1:

构造函数在定义对象的时候被调用;

论证如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

#include <stdio.h>

structTest

{

Test()

{

printf("你调用了构造函数\n");// 此处下断点、如果该函数被调用肯定会停下来。

}

};

intmain()

{

Test te;

printf("接力\n");

return0;

}

我们在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

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了构造函数\n");// 此处下断点、如果该函数被调用肯定会停下来。

}

Test(inta)

{

printf("重载%d\n",a);

}

Test(inta,intb)

{

printf("重载%d\n",a+b);

}

};

intmain()

{

Test te;

Test te1(1);

Test te2(1,1);// 注意、调用有参的构造函数时,需要传递参数

system("pause");

return0;

}

重载了两个,注意:调用有参数的构造时,需要传递参数。

运行可以通过:

证明3:

创建对象时默认调用的是无参构造;

论证如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <stdio.h>

#include <Windows.h>

structTest

{

Test(inta)

{

printf("重载%d\n",a);

}

Test(inta,intb)

{

printf("重载%d\n",a+b);

}

};

intmain()

{

Test te;// 普通定义对象的方式、不带参数

system("pause");

return0;

}

首先我们删除无参构造,看看能否编译通过:

不可以

然后删除有参构造:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了构造函数\n");// 此处下断点、如果该函数被调用肯定会停下来。

}

};

intmain()

{

Test te;

system("pause");

return0;

}

可以

全部都加上:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了构造函数\n");// 此处下断点、如果该函数被调用肯定会停下来。

}

Test(inta)

{

printf("重载%d\n",a);

}

Test(inta,intb)

{

printf("重载%d\n",a+b);

}

};

intmain()

{

Test te;

system("pause");

return0;

}

运行结果:

这已经证明了,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

#include <stdio.h>

#include <Windows.h>

structTest

{

intx;

inty;

intz;

Test()

{

}

Test(intx,inty,intz)// 构造函数初始化对象

{

this->x = x;

this->y = y;

this->z = z;

}

};

intmain()

{

Test te;

Test te1(1,2,3);// 定义对象并调用有参构造进行初始化

printf("%d %d %d\n",te1.x,te1.y,te1.z);// 输出看看是否初始化成功

system("pause");

return0;

}

运行如下:

初始化成功。

二、类的析构函数

什么是析构函数

类的构造函数名前加上'~'这个符号,就是类的析构函数

概念模糊、代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了一次类的构造函数\n");

}

~Test()

{

printf("你调用了一次类的析构函数\n");

}

};

intmain()

{

Test te;

// system("pause"); // 这里就不要让程序停下来了,不然析构不了

return0;

}

~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

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了一次类的构造函数\n");

}

~Test()

{

printf("你调用了一次类的析构函数\n");

}

};

intmain()

{

{

Test te;

printf("te生命周期即将结束。\n");

}// 析构应该在这里被调用

printf("te生命周期结束。\n");

system("pause");

return0;

}

运行结果如下:

断点

结果

证明成功。

证明3:

析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象;

已知堆栈中定义的对象(局部变量)在块语句结束之后就会被调用,那么带有return的main函数是在返回前调用析构,还是返回后呢?

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了一次类的构造函数\n");

}

~Test()

{

printf("你调用了一次类的析构函数\n");// 断点--汇编

}

};

intmain()

{

Test te;

// system("pause"); // 不要使用pause,不然无法返回

return0;

}

断点-调试-汇编:

可以看到是在函数返回前被调用的。

如果在全局区定义的对象呢?

这个问题很难说,像我一样定义两个断点就行了,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

#include <stdio.h>

#include <Windows.h>

structTest

{

Test()

{

printf("你调用了一次类的构造函数\n");

}

~Test()// 断点

{

printf("你调用了一次类的析构函数\n");

}

};

Test te;

intmain()

{

// system("pause"); // 不要使用pause,不然无法返回

return0;// 断点

}

运行:

发现一运行就断在了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

#include <stdio.h>

#include <Windows.h>

structTest

{

intx;

char* name;

Test()

{

name = (char*)malloc(sizeof(char)*20);// 构造时动态申请

}

~Test()

{

if(this->name!=0)// 析构时判断是个否为空,不为空释放

{

free(name);

name = 0;

}

}

};

intmain()

{

Test te;

return0;

}

复制讲解

这里我就不运行了,大家可以自己测试下。

总结

构造函数

1、和类具有相同名称,并且没有返回值类型的函数,就是类的构造函数

2、构造函数在定义对象的时候被调用

3、构造函数可以进行函数重载,可以有很多个

4、创建对象时默认调用的是无参构造

析构函数

1、类的构造函数名前加上'~'这个符号,就是类的析构函数

2、析构函数不能重载、不能有参数

3、析构函数在变量声明周期结束时被调用

4、析构函数被调用分两种情况:堆栈中定义的对象、全局区中定义的对象



http://www.jsqmd.com/news/873321/

相关文章:

  • 创业公司如何用 Taotoken 控制 AI 应用开发与测试成本
  • AutoUnipus:三步实现U校园自动化答题,100%正确率解放学习时间!
  • 企业财税合规实战:3步帮你重构账务数据,化解现金流风险
  • 告别静默推送!用UniApp+UniPush2.0打造高点击率消息通知(附完整项目配置)
  • 无需编程的13种语言文本挖掘工具:KH Coder完整指南带你5分钟上手
  • 成年人最大的牢笼:活在别人的操作系统里
  • 如何在苹果电脑上免费运行Windows应用:Whisky的完整指南
  • 书匠策AI拆解|毕业论文全流程“开挂指南“,2025届毕业生必看的写论文黑科技!
  • QGIS插件QuickOSM实战:5分钟搞定城市级路网、水系与POI数据,为你的地图项目快速打底
  • MyOS之Makefile入门
  • 14002开源:黄大年茶思屋 难题揭榜 第140期 非均匀雷达阵列的高精度高效率计算和排布算法 标准化解题写作框架黄大年茶思屋 难题揭榜 第140期
  • 银行业务学习:深入理解直贴(贴现)、转贴现、再贴现
  • 华硕笔记本性能控制终极指南:用G-Helper告别臃肿,重获系统掌控权
  • G-Helper终极指南:华硕笔记本显示优化与色彩管理深度解析
  • Python抖音机器人实战指南:3步实现智能颜值检测与自动化运营
  • 14003开源:黄大年茶思屋 难题揭榜 第140期 异构大规模资源协同分配多目标优化问题 标准化解题写作框架
  • 基于SpringBoot的旅游网站的设计与实现(源码+论文)
  • WeChatExporter深度解析:永久保存微信聊天记录的完整技术方案
  • 创建MQTT连接时如何设置参数?
  • 告别handshake timeout:手把手教你配置NVM镜像源,并附上Node.js各版本国内高速下载地址大全
  • 2026 年免费投票制作平台哪个最好用 丨平台深度测评报告 - 资讯纵览
  • 避坑指南:用MounRiver Studio一键烧录沁恒CH32V208时,WCH-LinkRV连接失败怎么办?
  • 工业计算机是什么?哪个好用?
  • 14004开源:黄大年茶思屋 难题揭榜 第140期 超大规模网络数据面编码建模 标准化解题写作框架
  • 高端设计制造业SD-WAN跨域组网深度解析:头部无人机企业如何实现异地数据高效安全回传和协作
  • 国产化项目实战:SpringBoot 2.6.2 + MyBatis-Plus 3.5.2 连接人大金仓Kingbase 8.6.0保姆级教程
  • Envoy支持Go Wasm插件,就真的更安全了吗?
  • 中国AI调用量是美国的2倍,但真正重要的不是这个数字
  • 2026年绵阳装修流程权威解读:透明装修开创者教你全程把控装修质量 - 优家闲谈
  • C++ Lambda 捕获陷阱:`[]` 与显式值捕获的线程安全之争