news 2026/5/15 0:39:34

C++默认成员函数

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++默认成员函数

构造函数

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象(局部对象在栈帧创建时,空间就开好了),而是对象实例化时初始化对象。构造函数的本质是要替代我们以前StackDate类中写的Init函数的功能,构造函数自动调用的特点就完美的替代的了Init。 构造函数有如下特点: 1. 函数名与类型同名 2. 可以重载 3. 没有返回值(不用写void) 4. 如果用户没有显式写构造函数,编译器会生成一个默认的无参构造函数,一旦用户显式定义编译器将不再生成。

代码语言:javascript

AI代码解释

// 构造函数 // 1. 函数名和类名同名 2. 可以重载 3. 没有返回值 4. 用户不写编译器会默认生成无参的构造函数 class Date { public: // 无参构造 Date() { _year = 1; _month = 1; _day = 1; } // 带参数构造 Date(size_t year, size_t month, size_t day) { _year = year; _month = month; _day = day; } //// 全缺省构造 //Date(size_t year = 1, size_t month = 1, size_t day = 1) //{ // _year = year; // _month = month; // _day = day; //} // void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: size_t _year; size_t _month; size_t _day; }; int main() { // 调用带参数的构造 Date d1(2025,7,5); d1.Print(); //// 无参构造和全缺省构造会产生调用歧义 //Date d2; //d2.Print(); // 无参的不能这么写 会和函数声明搞混 eg: void func // 这是函数声明还是函数定义呢? /*Date d2(); d2.Print();*/ //// 如果注释掉无参的构造和全缺省构造,会报错 //// C2512 没有合适的默认构造函数可用 //Date d2; //d2.Print(); // 调用无参的构造函数 Date d3; d3.Print(); return 0; }

默认构造函数分为三类:

  • 全缺省构造函数
  • 无参构造函数
  • 编译器默认生成的构造函数总结一下:不传参的构造函数就是默认构造函数,这三个函数不能同时存在 而全缺省构造函数和无参构造函数虽然构成函数重载,但是调用时会产生调用歧义我们不显式写构造函数,编译器默认生成的构造函数会如何处理数据?

代码语言:javascript

AI代码解释

using namespace std; class Time { public: Time() { _hour = 1; _minute = 1; _second = 1; } private: size_t _hour; size_t _minute; size_t _second; }; class Date { public: // 不写构造函数 编译器会自动生成默认构造函数 // 对于内置类型 编译器是否处理没有明确要求 // 对于自定义类型 调用该类型的默认构造函数 void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: size_t _year; size_t _month; size_t _day; Time _t; }; int main() { Date d1; d1.Print(); return 0; }

在这里插入图片描述

观察调试结果,我们可以得到如下结论: 对于编译器默认生成的构造函数,处理不同类型数据有不同行为:

  • 对于内置类型,编译器没有特别要求,对于VS环境,给出随机值
  • 对于自定义类型,该类型会调用它默认的构造函数 如果把Time类的无参构造函数注释掉,会有如下现象:

在这里插入图片描述

Time类调用它的默认构造函数,而Time类的默认构造函数是编译器生成的,又是处理内置类型,所以VS不做处理,给出随机值 针对这个问题C++11打了个补丁:内置类型成员变量在声明时给缺省值,用缺省值初始化

代码语言:javascript

AI代码解释

using namespace std; class Time { public: /*Time() { _hour = 1; _minute = 1; _second = 1; }*/ private: // C++11 在声明时给缺省值 size_t _hour = 1; size_t _minute = 1; size_t _second = 1; }; class Date { public: // 不写构造函数 编译器会自动生成默认构造函数 // 对于内置类型 编译器是否处理没有明确要求 // 对于自定义类型 调用该类型的默认构造函数 void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: // C++11 在声明时给缺省值 size_t _year = 1; size_t _month = 1; size_t _day = 1; Time _t; }; int main() { // 此时 Time类和Date类只有编译器默认生成的构造函数 Date d1; d1.Print(); return 0; }

![[Pasted image 20250707095835.png]]

总结:什么时候要显式定义构造函数?

  • 一般情况构造函数都要显式实现
  • 只有成员全为自定义类型的类不用显式实现

3. 析构函数

析构函数与构造函数功能相反,析构函数不是完成对对象本身的销毁,比如局部对象是存在栈帧的,函数结束栈帧销毁,他就释放了,不需要我们管,C++规定对象在销毁时会自动调用析构函数,完成对象中资源的清理释放工作。析构函数的功能类比我们之前Stack实现的Destroy功能,而像Date没有Destroy,其实就是没有资源需要释放,所以严格说Date是不需要析构函数的 析构函数有如下特点: 1. 函数名和类名相同,在函数名前加~2. 没有返回值 3. 不能重载,意味着一个类只有一个析构函数 4. 如果用户没有显式写,编译器会默认生成析构函数 5. 对象的生命周期结束,编译器自动调用析构函数

代码语言:javascript

AI代码解释

class Stack { public: Stack(size_t n = 4) { cout << "Stack(size_t n = 4) 析构" << endl; _arr = (int*)malloc(sizeof(int) * n); if (_arr == nullptr) { perror("malloc err!"); return; } _capacity = n; _top = 0; } ~Stack() { cout << "~Stack() 析构" << endl; assert(_arr); free(_arr); _arr = nullptr; _capacity = _top = 0; } private: int* _arr; int _capacity; int _top; }; int main() { Stack st1; return 0; }

和构造函数一样,如果我们不显式实现析构函数,编译器生成的析构函数对于内置类型不做处理,对于定义类型会调用它的析构函数,值得一提的是,是我们显式写析构函数,对于自定义类型成员也会调用他的析构,也就是说自定义类型成员无论什么情况都会自动调用析构函数

代码语言:javascript

AI代码解释

class tmp { public: ~tmp() { cout << "~tmp() 析构" << endl; } private: int _num; }; class Stack { public: Stack(size_t n = 4) { cout << "Stack(size_t n = 4) 构造" << endl; _arr = (int*)malloc(sizeof(int) * n); if (_arr == nullptr) { perror("malloc err!"); return; } _capacity = n; _top = 0; } /*~Stack() { cout << "~Stack() 析构" << endl; assert(_arr); free(_arr); _arr = nullptr; _capacity = _top = 0; }*/ private: int* _arr; int _capacity; int _top; tmp _t; }; int main() { Stack st1; return 0; }

我们可以通过调试观察:

在这里插入图片描述

总结:什么时候需要显式实现析构函数?

  • 有资源需要清理,就必须写析构函数,例如:StackList
  • 无资源要清理,可以不写
  • 内置类型成员没有资源要清理,剩下全是自定义类型,可以不写 还有一个重要的点:一个局部域的多个对象,后定义的先析构

代码语言:javascript

AI代码解释

设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?( ) C c; int main() { A a; B b; static D d; return 0; }
  1. 类的析构函数调用一般按照构造函数调用的相反顺序进行调用,但是要注意static对象的存在, 因为static改变了对象的生存作用域,需要等待程序结束时才会析构释放对象
  2. 全局对象先于局部对象进行构造
  3. 局部对象按照出现的顺序进行构造,无论是否为static
  4. 所以构造的顺序为c a b d
  5. 析构的顺序按照构造的相反顺序析构,只需注意static改变对象的生存作用域之后,会放在局部对象之后进行析构
  6. 因此析构顺序为B A D C

4. 拷贝构造函数

拷贝构造函数的第一个参数是自身类型的引用,且任何额外的参数都有缺省值,这样的函数叫做拷贝构造函数,用于同类对象的拷贝初始化,是构造函数的重载。 本文以最常规情况的拷贝构造函数展开,即有且仅有一个参数:类类型对象的引用拷贝构造函数有如下特点:

  • 拷贝构造函数是构造函数的一个重载
  • 拷贝构造函数的第一个参数必须是类类型对象的引用,使用传值方式编译器会报错(会引发无穷递归调用),拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后面的参数必须有缺省值

代码语言:javascript

AI代码解释

// 拷贝构造函数 // 构造函数的重载,第一个参数必须是类类型对象的引用 // 用于同类对象的拷贝初始化 class Date { public: Date() { _year = 1; _month = 1; _day = 1; } Date(Date& d) { cout << "call Date(Date& d)" << endl; _year = d._year; _month = d._month; _day = d._day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: size_t _year; size_t _month; size_t _day; }; int main() { Date d1; // 两种写法都可以 Date d2 = d1; // d是d1的别名,d3是this指针 Date d3(d1); d1.Print(); d2.Print(); d3.Print(); return 0; }

再来看一段代码:

代码语言:javascript

AI代码解释

Date(Date& d) { cout << "call Date(Date& d)" << endl; // 如果不小心写反了会发生什么? d._year = _year; d._month = _month; d._day = _day; }

其余部分不变

在这里插入图片描述

初始的d1也被修改成随机值了,我们进行拷贝构造,提供拷贝值的对象是不能被修改的,所以为了防止这样的情况发生,我们做如下处理:Date(const Date& d)保证d的只读性

代码语言:javascript

AI代码解释

// 拷贝构造函数 // 构造函数的重载,第一个参数必须是类类型对象的引用 // 用于同类对象的拷贝初始化 class Date { public: Date() { _year = 1; _month = 1; _day = 1; } Date(const Date& d) { cout << "call Date(Date& d)" << endl; _year = d._year; _month = d._month; _day = d._day; } void Print() { cout << _year << "-" << _month << "-" << _day << endl; } private: size_t _year; size_t _month; size_t _day; }; int main() { Date d1; // 两种写法都可以 Date d2 = d1; // d是d1的别名,d3是this指针 Date d3(d1); d1.Print(); d2.Print(); d3.Print(); return 0; }
  • C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以自定义类型传值传参和传值返回都会调用拷贝构造
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/15 0:38:36

Adafruit LED街机按钮I2C控制板:简化交互项目布线的智能IO方案

1. 项目概述与核心价值 如果你正在为一个交互项目寻找一种既能简化布线、又能提供炫酷灯光反馈的按钮解决方案&#xff0c;那么Adafruit这款LED Arcade Button 1x4 STEMMA QT I2C控制板绝对值得你深入研究。它本质上是一个高度集成的“按钮与灯光管理器”&#xff0c;把四个独立…

作者头像 李华
网站建设 2026/5/15 0:38:03

【软考网络工程师综合分析题整理(一)】

TOC 1.区块链&#xff1a;①区块链的数据块是按时间顺序排列&#xff0c;并生成永久、不可修改的记录 ②区块链中的每个数据块包含了网络交易的一部分信息&#xff0c;但不一定是全部信息&#xff0c;信息可能会分散在多个区块中 ③区块链技术最初是作为比特币的底层技术被提出…

作者头像 李华
网站建设 2026/5/15 0:34:12

LoRa物联网通信实战:从RFM9x模块引脚配置到Arduino/CircuitPython驱动开发

1. 项目概述与LoRa技术核心如果你正在寻找一种能让你的物联网设备在几公里外还能“说上话”&#xff0c;同时又不想让电池在几小时内耗尽的技术&#xff0c;那么LoRa&#xff08;Long Range&#xff0c;远距离&#xff09;几乎就是为你量身定做的。我最早接触LoRa是在一个农业监…

作者头像 李华
网站建设 2026/5/15 0:32:25

GoTrain 项目开发指南项目架构

GoTrain 项目开发指南1 make code tableusers 2 cd ./goapi/data/code/internal/dbentity 3 copy entity ,dao至beapi dbdao dbentity 4 ai生成uiTableRequest 对于公共代码可以开发 apiservice apifacade domainfacade 5 ai生成TableController 6 编写swag 7 测试 8 make doc生…

作者头像 李华
网站建设 2026/5/15 0:21:21

2025最权威的六大降AI率工具横评

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 具备强大功能的降AI功耗优化工具&#xff0c;可针对主流AI推理场景&#xff0c;显著降低运行…

作者头像 李华
网站建设 2026/5/15 0:20:47

为内部知识库问答系统集成Taotoken的多模型能力

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 为内部知识库问答系统集成Taotoken的多模型能力 在构建企业级智能客服或内部知识库系统时&#xff0c;一个核心挑战是如何平衡回答…

作者头像 李华