news 2026/3/1 0:41:30

类和对象(三)-默认成员函数详解与运算符重载

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
类和对象(三)-默认成员函数详解与运算符重载
hello,这里是AuroraWanderll。 兴趣方向:C++,算法,Linux系统,游戏客户端开发 欢迎关注,我将更新更多相关内容!

我的个人主页
这是类和对象系列的第三篇文章,上篇指引:
类和对象(二)访问限定符-类的实例化与this指针

类和对象(三)-默认成员函数详解与运算符重载

简易目录

  • 类的6个默认成员函数概述
  • 构造函数详解
  • 析构函数详解

1. 类的6个默认成员函数概述

核心概念:当一个类中什么成员都没有时(称为空类),即用户没有显式实现时,编译器会自动生成6个默认成员函数。

class Date {}; // 看似空的类,实际上编译器会生成6个默认成员函数
序号默认成员函数基本作用
1构造函数对象创建时自动调用,用于初始化对象
2析构函数对象销毁时自动调用,用于清理资源
3拷贝构造函数用同类型的已有对象初始化新对象,如v1(v2)
4拷贝赋值运算符将一个对象的值赋给另一个同类型对象,如v1=v2
5移动构造函数(C++11)通过"移动"资源来初始化新对象,避免不必要的拷贝
6移动赋值运算符(C++11)通过"移动"资源来赋值,避免不必要的拷贝
7取地址重载运算符

其中5,6相对比较进阶,本篇不会提及.

2. 构造函数

2.1 构造函数的概念

背景:为什么我们要有构造函数?

答:C语言传统初始化方式繁琐

class Date { public: void Init(int year, int month, int day) { _year = year; _month = month; _day = day; } // ... 其他成员 }; int main() { Date d1; d1.Init(2022, 7, 5); // 每次创建对象后都要手动调用初始化函数Init,未免太过麻烦 return 0; }

构造函数定义:特殊的成员函数,在创建对象时自动调用,用于初始化对象数据成员,整个生命周期只调用一次。

2.2 构造函数的特性

基本特征

  1. 函数名与类名相同

  2. 无返回值

  3. 对象实例化时自动调用

  4. 重载(一个对象可以有多个不同的构造函数)

    需要注意的是:虽然叫构造函数,但是它并不负责开空间创建对象,它的主要工作是初始化对象

class Date { public: // 1. 无参构造函数 Date() {} // 2. 带参构造函数(重载) Date(int year, int month, int day) { _year = year; _month = month; _day = day; } private: int _year; int _month; int _day; }; void TestDate() { Date d1; // 调用无参构造函数 Date d2(2015, 1, 1); // 调用带参构造函数 // 注意:无参构造不能加括号,否则变成函数声明 Date d3(); // 错误:声明了d3函数,而非创建对象 }

编译器自动生成规则

  • 如果类中没有显式定义构造函数,编译器自动生成无参默认构造函数
  • 一旦用户显式定义任何构造函数,编译器不再生成默认构造函数
class Date { // 如果用户显式定义构造函数,编译器不再生成默认构造函数 // Date(int year, int month, int day) { ... } private: int _year; int _month; int _day; }; int main() { Date d1; // 如果屏蔽自定义构造函数,编译通过;如果放开,编译失败 return 0; }

默认构造函数的作用

看起来编译器自动生成的默认构造函数没有作用,例如int类型的参数,默认构造之后依旧是随机值。实际上默认构造函数是会根据类型来进行不同的初始化的

  • 对内置类型(int、char等):不处理(C++11前)或使用默认值(C++11后)
  • 对自定义类型:调用其默认构造函数
class Time { public: Time() // Time类的构造函数 { cout << "Time()" << endl; _hour = 0; _minute = 0; _second = 0; } private: int _hour; int _minute; int _second; }; class Date { private: // 内置类型成员,C++11之前不处理 int _year; int _month; int _day; // 自定义类型成员 Time _t; // 编译器生成的默认构造函数会调用Time的构造函数 }; int main() { Date d; // 调用Date的默认构造函数,同时会调用Time的构造函数 return 0; }

C++11改进:内置类型成员可以在声明时给默认值

class Date { private: // 内置类型成员给默认值,C++11之后,直接初始化成我们给的默认值 int _year = 1970; int _month = 1; int _day = 1; Time _t; // 自定义类型 };

默认构造函数规则

  • 无参构造函数、全缺省构造函数、编译器生成的构造函数都算默认构造函数
  • 默认构造函数只能有一个
class Date { public: // 无参构造函数(默认构造函数) Date() { _year = 1900; _month = 1; _day = 1; } // 全缺省构造函数(也是默认构造函数) Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } };

如果我们在类的对象中同时写了超过一个的默认构造,那么它就会报错

编译错误原因:

当执行Date d1;时,编译器面临选择困难:

  • 可以调用无参构造函数Date()
  • 也可以调用全缺省构造函数Date(1900, 1, 1)(使用默认参数)

两个函数都匹配,编译器无法确定该调用哪一个,因此报编译错误

正确写法:

方案1:只保留一个默认构造函数

class Date { public: // 只保留全缺省构造函数(推荐) Date(int year = 1900, int month = 1, int day = 1) { _year = year; _month = month; _day = day; } };

方案2:使用不同的参数列表

class Date { public: // 无参构造函数 Date() : _year(1900), _month(1), _day(1) {} // 带参构造函数(不是全缺省) Date(int year, int month, int day) { _year = year; _month = month; _day = day; } };

可以简单理解默认构造函数是:调用时不需要传递参数的构造函数


总结要点

  • 空类会自动获得6个默认成员函数
  • 构造函数在对象创建时自动调用,用于初始化
  • 构造函数可以重载,名称与类名相同且无返回值
  • 编译器在特定条件下自动生成默认构造函数
  • 默认构造函数对内置类型和自定义类型的处理方式不同
  • C++11允许内置类型成员在声明时给默认值

3. 析构函数详解

3.1 析构函数的概念

核心问题:对象是如何被销毁的?

析构函数定义:与构造函数功能相反,但不是完成对象本身的销毁(局部对象的销毁由编译器完成),而是在对象销毁时自动调用,完成对象中资源的清理工作

3.2 析构函数的特性

基本特征:
  1. 函数名:类名前加上~
  2. 参数和返回值:无参数、无返回值类型
  3. 唯一性:一个类只能有一个析构函数,不能重载
  4. 调用时机:对象生命周期结束时自动调用
class Stack { public: // 构造函数:申请资源 Stack(size_t capacity = 3) { _array = (int*)malloc(sizeof(int) * capacity); if (NULL == _array) { perror("malloc申请空间失败!!!"); return; } _capacity = capacity; _size = 0; } // 析构函数:释放资源 ~Stack() { if (_array) { free(_array); // 释放动态内存 _array = NULL; // 防止野指针 _capacity = 0; // 重置容量 _size = 0; // 重置大小 } } void Push(int data) { _array[_size] = data; _size++; } private: int* _array; int _capacity; int _size; }; void TestStack() { Stack s; // 构造函数自动调用 s.Push(1); s.Push(2); // 函数结束时,s的析构函数自动调用,释放内存 }
编译器生成的析构函数

重要特性:编译器生成的默认析构函数会对自定义类型成员调用其析构函数。

class Time { public: ~Time() { cout << "~Time()" << endl; // 析构时输出信息 } private: int _hour; int _minute; int _second; }; class Date { private: // 内置类型成员 int _year = 1970; int _month = 1; int _day = 1; // 自定义类型成员 Time _t; // 包含Time类对象 }; int main() { Date d; // 创建Date对象 return 0; } // d销毁时,输出:~Time()

运行结果解释

  • 虽然main函数中没有直接创建Time对象
  • Date对象d包含Time成员_t
  • d销毁时,编译器为Date生成的默认析构函数会自动调用Time类的析构函数
析构函数的调用规则

关键原则

  • 创建哪个类的对象,就调用该类的构造函数
  • 销毁哪个类的对象,就调用该类的析构函数
  • 编译器生成的析构函数会保证所有成员都能正确销毁

3.3 何时需要编写析构函数

不需要编写的情况:
class Date { private: int _year = 1970; int _month = 1; int _day = 1; // 只有内置类型,无动态资源,使用编译器生成的析构函数即可 };
必须编写的情况:
class Stack { private: int* _array; // 动态分配的内存 int _capacity; int _size; public: // 必须编写析构函数来释放动态内存 ~Stack() { if (_array) { free(_array); _array = NULL; } } };

也就是说不是说自定义类型就一定需要写析构函数,关键在于你的类型之中是否动态申请资源。

资源泄漏风险:如果类中申请了资源(动态内存、文件句柄、网络连接等)但没有编写析构函数,会导致资源泄漏。

3.4 实际应用场景

场景1:动态数组管理
class DynamicArray { private: int* _data; size_t _size; public: DynamicArray(size_t size) : _size(size) { _data = new int[size]; // 动态分配 } ~DynamicArray() { delete[] _data; // 必须释放 _data = nullptr; } };
场景2:文件资源管理
class FileHandler { private: FILE* _file; public: FileHandler(const char* filename) { _file = fopen(filename, "r"); } ~FileHandler() { if (_file) { fclose(_file); // 必须关闭文件 _file = nullptr; } } };

总结:

  1. 析构函数作用:对象销毁时自动调用,用于资源清理
  2. 语法特征~类名(),无参无返回值,不能重载
  3. 调用时机:对象生命周期结束时自动调用
  4. 编译器行为:默认生成的析构函数会调用自定义类型成员的析构函数
  5. 编写原则:有资源申请时必须编写,无资源时可依赖编译器生成
  6. 资源管理:防止内存泄漏、文件未关闭等资源管理问题

核心思想:谁申请,谁释放;构造函数申请资源,析构函数释放资源,形成完整的资源管理生命周期。

感谢你能够阅读到这里,如果本篇文章对你有帮助,欢迎点赞收藏支持,关注我, 我将更新更多有关C++,Linux系统·网络部分的知识。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/28 17:36:59

Kotaemon重排序模型(Re-Ranker)集成教程

Kotaemon重排序模型集成深度指南 在构建企业级智能问答系统时&#xff0c;一个常见的痛点是&#xff1a;即便使用了强大的大语言模型&#xff08;LLM&#xff09;&#xff0c;系统仍可能给出看似合理却与实际政策或知识不符的回答。这种“幻觉”问题在金融、医疗、人力资源等高…

作者头像 李华
网站建设 2026/2/26 20:53:34

Unity学习笔记(二十)PlayerPrefs(一)

目录 PlayerPrefs是什么 存储原理 读取相关 删除数据 PlayerPrefs数据唯一性 PlayerPrefs是什么 是Unity提供的可以用于存储读取玩家数据的公共类 存储原理 PlayerPrefs的数据存储&#xff0c;类似键值对存储&#xff0c;一个键对应一个值 提供了存储3种数据的方法 &am…

作者头像 李华
网站建设 2026/2/18 10:09:21

书籍-严羽《沧浪诗话》

严羽《沧浪诗话》详细介绍 书籍基本信息 书名&#xff1a;沧浪诗话 作者&#xff1a;严羽&#xff08;南宋&#xff09; 成书时间&#xff1a;南宋理宗时期&#xff08;约公元1230-1250年&#xff09; 卷数&#xff1a;1卷&#xff08;分五章&#xff09; 类别&#xff1a;诗歌…

作者头像 李华
网站建设 2026/2/28 7:06:36

2025年有哪些好用的PDF编辑器推荐?今天带来5款深度测评

数字化办公的深化让PDF编辑器从“可选工具”变为“核心装备”&#xff0c;但用户选购时常常陷入“功能看似全却不好用”“符合场景却不安全”的矛盾——企业需要兼顾协作与数据安全&#xff0c;个人追求高效与低成本&#xff0c;政府则聚焦合规与国产化。基于2025年最新市场需求…

作者头像 李华
网站建设 2026/2/7 20:11:51

构筑测试事业的北极星——软件测试愿景制定指南

为什么测试团队需要明确的愿景&#xff1f; 在瞬息万变的软件开发领域中&#xff0c;软件测试作为质量保障的核心环节&#xff0c;常常陷入被动响应的工作模式——追逐不断变更的需求&#xff0c;应对持续迭代的产品&#xff0c;处理突发的线上问题。这种被动姿态不仅使测试人…

作者头像 李华
网站建设 2026/2/21 23:20:43

如何在没有经费的情况下做科研

实验的家伙事 在做这个工作之前&#xff0c;笔者的主业是合成生物学与分子生物学&#xff0c;见我之前的博文《质粒拷贝数以及启动子活性的定量测量》。在研究过程中有一些发现&#xff0c;也积累了几个点子和想法。然而这些想法无一实现&#xff0c;主要问题是没有钱和资源来…

作者头像 李华