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

C++修炼之构造函数与析构函数

🌷默认成员函数

上一章中我们谈到,如果一个类中什么成员也没有,那么这个类就叫作空类。其实这么说是不太严谨的,因为一个类不可能什么都没有

当我们定义好一个类,不做任何处理时,编译器会自动生成以下6个默认成员函数

  • 默认成员函数:如果用户没有手动实现,则编译器会自动生成的成员函数

  • 构造函数:主要完成初始化工作;

  • 析构函数:主要完成清理工作;

  • 拷贝构造:使用一个同类的对象初始化创建一个对象;

  • 赋值重载:把一个对象赋值给另一个对象;

  • 取地址重载普通对象取地址操作;

  • 取地址重载(const):const对象取地址操作;

本章我们将学习两个默认成员函数——构造函数析构函数

🌷构造函数

🌺引例

在C语言阶段,我们实现的数据结构时,有一件事很苦恼,就是每当创建一个stack对象(之前叫作定义一个stack类型的变量)后,首先得调用它的专属初始化函数StackInit来初始化对象。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

typedefintdataOfStackType;

typedefstructstack

{

dataOfStackType* a;

inttop;

intcapacity;

}stack;

voidStackInit(stack* ps);

//...

intmain()

{

stack s;

StackInit(&s);

//...

return0;

}

这不免让人觉得有点麻烦。在C++中,构造函数为我们很好的解决了这一问题。

🌺构造函数的概念及特性

构造函数是一个特殊的成员函数。构造函数虽然叫作构造,但是其主要作用并不是开辟空间创建对象,而是初始化对象

构造函数之所以特殊,是因为相比于其它成员函数,它具有如下特性

  1. 函数名与类名相同
  2. 无返回值
  3. 对象实例化时,编译器自动调用对应的构造函数
  4. 构造函数可以重载

🌼举例🌼

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

classDate

{

public:

//无参的构造函数

Date()

{};

//带参的构造函数

Date(intyear,intmonth,intday)

{

_year = year;

_month = month;

_day = day;

}

private:

int_year;

int_month;

int_day;

};

voidTestDate()

{

Date d1;//调用无参构造函数(自动调用)

Date d2(2023, 3, 29);//调用带参构造函数(自动调用)

}

🌼特别注意🌼

  • 创建对象时编译器会自动调用构造函数,若是调用无参构造函数,则无需在对象后面使用()。否则会产生歧义:编译器无法确定你是在声明函数还是在创建对象

🌼错误示例🌼

1

2

//错位示例

Date d3();

  1. 如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

classDate

{

public:

//若用户没有显示定义,则编译器自动生成。

/*Date(int year,int month,int day)

{

_year = year;

_month = month;

_day = day;

}*/

private:

int_year;

int_month;

int_day;

};

  1. 默认生成构造函数,对内置类型成员不作处理;对自定义类型成员,会调用它的默认构造函数
  • C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int、char、double…,自定义类型就是我们使用class、struct、union等自己定义的类型。

🌼举例🌼

🌼默认构造函数对内置类型🌼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

classDate

{

public:

//此处不对构造函数做显示定义,测试默认构造函数

/*Date()

{}*/

voidprint()

{

cout << _year <<"-"<< _month <<"-"<< _day << endl;

}

private:

int_year;

int_month;

int_day;

};

voidTestDate1()

{

Date d1;

d1.print();

}

  • 如图所示,默认构造函数的确未对内置类型做处理。

🌼默认构造函数对自定义类型🌼

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

26

27

28

29

30

classstack

{

public:

//此处对stack构造函数做显示定义

stack()

{

cout <<"stack()"<< endl;

_a = nullptr;

_top = _capacity = 0;

}

private:

int* _a;

int_top;

int_capacity;

};

classqueue

{

public:

//此处不对queue构造函数做显示定义,测试默认构造函数

/*queue()

{}*/

private:

//自定义类型成员

stack _s;

};

voidTestQueue()

{

queue q;

}

  • 如图所示,在创建queue对象时,默认构造函数对自定义成员_s做了处理,调用了它的默认构造函数stack()

这一波蜜汁操作让很多C++使用者感到困惑与不满,为什么要针对内置类型和自定义类型做不同的处理呢?终于,在C++11中针对内置类型成员不初始化的缺陷,又打了补丁,即:

  1. 内置类型成员变量在类中声明时可以给默认值

🌼举例🌼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

classDate

{

public:

//...

voidprint()

{

cout << _year <<"-"<< _month <<"-"<< _day << endl;

}

private:

//使用默认值

int_year = 0;

int_month = 0;

int_day = 0;

};

voidTestDate2()

{

Date d2;

d2.print();

}

  • 默认值:若不对成员变量做处理,则使用默认值。
  1. 无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个

🌼举例🌼

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

26

classDate

{

public:

//无参的默认构造函数

//Date()

//{

//}

//全缺省的默认构造函数

Date(intyear = 0,intmonth = 0,intday = 0)

{

_year = year;

_month = month;

_day = day;

}

voidprint()

{

cout << _year <<"-"<< _month <<"-"<< _day << endl;

}

private:

int_year = 0;

int_month = 0;

int_day = 0;

};

🌷析构函数

析构函数构造函数的特性相似,但功能有恰好相反。构造函数是用来初始化对象的,析构函数是用来销毁对象的。

  • 需要注意的是,析构函数并不是对对象本身进行销毁(因为局部对象出了作用域会自行销毁,由编译器来完成),而是在对象销毁时会自动调用析构函数,对对象内部的资源做清理(例如stack _s中的int* a)。

同样,有了析构函数,我们再也不用担心创建对象(或定义变量)后由于忘记释放内存而造成内存泄漏了。

🌼举例🌼

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

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

classStack

{

public:

Stack()

{

//...

}

voidPush(intx)

{

//...

}

boolEmpty()

{

// ...

}

intTop()

{

//...

}

voidDestory()

{

//...

}

private:

// 成员变量

int* _a;

int_top;

int_capacity;

};

voidTestStack()

{

Stack s;

st.Push(1);

st.Push(2);

//过去需要手动释放

st.Destroy();

}

🌺析构函数的特性

  1. 析构函数名是在类名前加上字符~
  2. 无参数
  3. 无返回值
  4. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数
  5. 析构函数不能重载

🌼举例🌼

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

classDate

{

public:

Date()

{

cout <<"Date()"<< endl;

}

~Date()

{

cout <<"~Date()"<< endl;

}

private:

int_year = 0;

int_month = 0;

int_day = 0;

};

voidTestDate3()

{

Date d3;

//d3生命周期结束时自动调用构造函数

}

为便于观察,我们可以在析构函数内部写点儿东西。

  1. 编译器生成的默认析构函数,对自定类型成员调用它的析构函数

🌼举例🌼

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

26

27

28

29

30

31

32

33

34

35

36

37

classstack

{

public:

//此处对stack构造函数做显示定义

stack()

{

cout <<"stack()"<< endl;

_a = nullptr;

_top = _capacity = 0;

}

~stack()

{

cout <<"~Stack()"<< endl;

free(_a);

_a = nullptr;

_top = _capacity = 0;

}

private:

int* _a;

int_top;

int_capacity;

};

classqueue

{

public:

//此处不对queue构造函数做显示定义,测试默认构造函数

/*queue()

{}*/

private:

//自定义类型成员

stack _s;

};

voidTestQueue1()

{

queue q;

}

  • 这里可能有小伙伴会好奇:为什么析构函数不像构造函数那样区分内置类型与自定义类型呢
    答案是:因为内置类型压根不需要我们担心清理工作,在其生命周期结束时会自动销毁。而自定义类型需要担心,因为自定义类型里可能含有申请资源(例如:malloc申请内存须手动释放)。
http://www.jsqmd.com/news/873192/

相关文章:

  • ClassIn 在 Linux 下无法播放音频
  • 直播预告 - 周日晚 7 点半-AI 驱动 UI 自动化
  • AI智能体应用工程师报名流程拆解:学习、考试、证书查询一次说清 - 精选教育培训热点
  • CANN ops-transformer:MC2 通信融合算子怎么加速 MoE 的 All-to-All
  • 模块化多电平变流器快速排序与降低开关频率的方法与应用【附案例】
  • 西恩士液冷板清洁度检测设备方案提供:不只是卖设备,更是交付能力 - 工业设备研究社
  • 一文带你学习C++析构函数
  • 2026适合小白的高还原度PDF转长图工具推荐合集 - 时讯资讯
  • 宝塔域名已经添加了,但ssl里面没有
  • 如何在Windows 11上快速安装安卓子系统:3步开启跨平台应用新时代
  • 安顺外贸网站建设 B2B 建站定制,WaiMaoYa 外贸鸭专业跨境建站机构 - 外贸营销工具
  • CANN-昇腾NPU-多机多卡-怎么把16卡用出32卡的效果
  • 2026年5月诚信的阻燃电缆沟盖板厂家,免费样品测试助力客户精准选型适配项目 - 品牌鉴赏师
  • P4777 【模板】扩展中国剩余定理(EXCRT)题解
  • 基于Java的外卖点餐配送系统_43lq510m
  • agent memory论文解析一:解析项目(a-mem)
  • DDrawCompat终极指南:简单三步让老游戏在Win10/11完美运行
  • Topit终极指南:如何在Mac上实现高效窗口置顶,提升300%工作效率
  • 西恩士液冷板清洁度分析仪装置 - 工业设备研究社
  • 基站正在成为 AI 计算节点:NVIDIA Aerial 推动 RAN 架构重构
  • MPC5604B/C 信号与引脚全解|硬件 / 底层必看
  • 写给新手的 asnumpy:昇腾原生 NumPy 到底是啥?
  • 题解:luogu P8996([CEOI 2022] Abracadabra)
  • 今天不建Lovable ML平台,明天就被团队弃用!2025年AI工程团队留存率预警下的4步速建法
  • AI浪潮下,软件开发行业的深度变革与未来走向
  • 深夜办公不掉链:2026免费PDF转PPT工具Top榜 - 时讯资讯
  • 投影仪的分辨率不高,仅为1024*768的分辨率,而笔记本电脑2560×1600(2.5K)分辨率。‌‌——如果采用扩展屏复制笔记本电脑分辨率,发现那个投影仪投影出的字很小,且看不清。 将笔记本电脑的
  • DriverStore Explorer终极指南:Windows驱动清理与管理的完整解决方案
  • 龙芯3A5000工业主板实战:从硬件部署到软件生态的国产化替代指南
  • 给机器人一个值得信赖的“判断力”