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/15 19:31:49

【RL】LoRA Without Regret

https://thinkingmachines.ai/blog/lora/ 当今领先的语言模型包含多达万亿个参数&#xff0c;并在数以十万亿计的词元&#xff08;tokens&#xff09;上进行了预训练。 基础模型的性能随着规模的扩大而不断提升&#xff0c;因为这万亿级的参数对于学习和表征所有已记录下来的人…

作者头像 李华
网站建设 2026/4/15 21:42:44

14、Samba用户、安全与域配置全解析

Samba用户、安全与域配置全解析 1. 用户与组配置 在使用Samba服务器时,若使用Windows 98或NT 4.0 Workstation SP3连接,需先为服务器配置加密密码,否则客户端将拒绝连接。这是因为这些Windows客户端会发送加密密码,Samba需配置为能接收并解密。 1.1 单用户配置 为客户端…

作者头像 李华
网站建设 2026/4/12 10:11:00

华为OD机考双机位B卷 - 贪吃的猴子 (Java Python JS C/C++ GO )

最新华为上机考试 真题目录&#xff1a;点击查看目录 华为OD面试真题精选&#xff1a;点击立即查看 2025华为od机试双机位B卷 题目描述 只贪吃的猴子&#xff0c;来到一个果园&#xff0c;发现许多串香蕉排成一行&#xff0c;每串香蕉上有若干根香蕉。每串香蕉的根数由数组…

作者头像 李华
网站建设 2026/4/16 5:56:33

通达信正版庄见愁+海底活鱼

{}KS13:(C-SMA(C,13,1))/SMA(C,13,1)*(-100); {} AAA:REF(KS13,1)>13 AND REF(KS13,1)/KS13>1.23 AND C/REF(C,1)>1.03; 海底活鱼:IF(AAA AND REF(C,1)<COST(5),30,0),COLORWHITE,LINETHICK2; STICKLINE(庄筹线>散筹线,庄筹线,散筹线,0,0),COLORRED;

作者头像 李华
网站建设 2026/4/16 6:04:14

51的复位电路和时钟电路

对于模电数电没啥基础的人&#xff0c;电路一下真看不懂一、复位电路时间常数R*C10k*10u10*1000*0.1*0.001*0.0010.001s1ms1000us给RST引脚两个机器周期以上的高电平&#xff0c;复位。对于12M晶振&#xff0c;机器周期是1us&#xff08;微秒&#xff09;&#xff0c;1000us远大…

作者头像 李华