news 2026/4/15 16:42:46

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

作者头像

张小明

前端开发工程师

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/4/13 5:02:42

场效应管通电短路

场效应管通电短路是指MOS管在上电瞬间或工作过程中&#xff0c;漏极&#xff08;D&#xff09;与源极&#xff08;S&#xff09;之间失去阻断能力&#xff0c;呈现极低电阻&#xff08;通常<1Ω&#xff09;的失效状态。这是电力电子系统中最严重的故障之一&#xff0c;可能…

作者头像 李华
网站建设 2026/4/15 10:33:22

19、Samba使用指南:名称解析与额外功能配置

Samba使用指南:名称解析与额外功能配置 1. Samba名称解析概述 在NetBIOS名称服务器(NBNS)出现之前,名称解析完全依靠广播。若要获取某台机器的地址,只需在网络中广播其名称,理论上该机器会作出回应。例如,若要查找名为“fred”的机器,仍可通过广播查询来确定其是否存在…

作者头像 李华
网站建设 2026/4/10 13:47:23

无代码解决方案:解锁数字化转型的普惠路径

在数字化转型进入深水区的当下&#xff0c;企业对数字化工具的核心诉求已从“功能完备”转向“快速适配、低成本落地、业务主导”。传统代码开发模式因周期长、成本高、技术门槛高的弊端&#xff0c;难以满足中小企业和业务部门的灵活需求。无代码解决方案以可视化配置、拖拽式…

作者头像 李华
网站建设 2026/4/2 2:36:57

YMatrix 高可用详解:3 种镜像策略在节点宕机时表现有何不同?

前言 不同镜像策略如何对集群高可用表现产生影响&#xff1f; 在数据库中&#xff0c; 高可用性是保障业务连续性的核心——一旦 Primary 节点故障&#xff0c;能否快速切换到备份节点&#xff0c;直接决定了业务的“抗风险能力”。YMatrix 的 Mirror 机制正是实现这一目标的…

作者头像 李华
网站建设 2026/4/5 14:51:29

matlab进行利用遗传算法对天线阵列进行优化

在MATLAB中利用遗传算法&#xff08;Genetic Algorithm, GA&#xff09;对天线阵列进行优化是一种常见的方法&#xff0c;特别适用于解决多目标优化问题、天线方向图形状优化、波束形成等问题。下面将使用MATLAB的遗传算法工具箱&#xff08;Global Optimization Toolbox&#…

作者头像 李华