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

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++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以自定义类型传值传参和传值返回都会调用拷贝构造
http://www.jsqmd.com/news/818538/

相关文章:

  • 5 分钟判断:你更需要 TTS 还是 STT?
  • 别光看Nordic了!用Zephyr OS的蓝牙协议栈,在国产MCU上也能玩转BLE
  • 2025-2026年北京办公室装饰装修公司推荐:五家排行评测专注科技企业办公区防工期延误 - 品牌推荐
  • 创业团队如何利用多模型聚合平台优化AI应用开发成本
  • 2026年5月节能模压桥架选型攻略:聚焦可靠供应商与核心优势 - 2026年企业推荐榜
  • 2026现阶段浙江马克笔厂商甄选指南:以温州中锐为例剖析核心竞争力 - 2026年企业推荐榜
  • 告别城通网盘限速:三步获取高速直连地址的终极方案
  • 为什么你的Turbo模式响应延迟仍超8秒?揭秘Midjourney官方未公开的4层排队机制与实时带宽抢占策略
  • 2026南充靠谱装修公司盘点:南充整装装修、南充新房装修、南充旧房改造、南充本地装修公司、南充环保装修、南充硬装装修选择指南 - 优质品牌商家
  • 本地知识库liz:基于RAG的智能文档检索工具部署与调优指南
  • 2026年5月深圳除甲醛公司推荐:五家品牌评测对比办公室除醛防眼干 - 品牌推荐
  • 性能测试从入门到精通:这3个工具+5个技巧,让你快速上手
  • 同花顺问财数据获取终极指南:Python量化分析的高效解决方案
  • Vue项目打包上线前,别忘了用terser-webpack-plugin清理console和注释(Webpack 4/5配置详解)
  • 2026福州VR交互式展示避坑实测:TOP4权威认证选择指南
  • 2026年5月国际十大物流公司排行榜推荐:十家专业评测夜班跨境货物追踪不丢件 - 品牌推荐
  • 2025-2026年国内空气净化器品牌推荐:五款排名产品专业评测解决儿童房装修致甲醛刺鼻 - 品牌推荐
  • 2026年长途整车专线物流公司排行及品牌甄选指南:跨省汽车托运公司电话/跨省零担专线物流公司价格/长途汽车托运公司多少钱/选择指南 - 优质品牌商家
  • Vue项目性能优化的全流程指南
  • 2026年5月提干辅导培训机构前十名推荐:十大排名产品评测夜间备考防效率低下 - 品牌推荐
  • 安全测试的核心技能:掌握这4个方法,成为测试领域的稀缺人才
  • d2dx:让经典《暗黑破坏神2》在现代PC上重获新生的魔法引擎
  • 2026游乐场无动力游乐设备厂家推荐:四川无动力游乐设备厂家、室内无动力游乐设备、室外无动力游乐设备、小型无动力游乐设备选择指南 - 优质品牌商家
  • 2026年5月新消息:哈尔滨企业短视频服务团队口碑*,翰诺科技为何持续领跑? - 2026年企业推荐榜
  • 10分钟精通APK安装器:Windows系统无缝运行安卓应用的完整指南
  • 2026年5月北京办公室装饰装修公司推荐:五家专业评测专注科技总部场景防工期延误 - 品牌推荐
  • 嵌入式开发串口通信与USB连接故障排查实战指南
  • 还在为繁琐的游戏准备浪费时间?League Akari 让英雄联盟体验智能化升级
  • 2026杭州眼镜店TOP5技术评测:杭州配眼镜、武汉眼镜店、武汉配眼镜、深圳眼镜店、深圳配眼镜、苏州眼镜店、苏州配眼镜选择指南 - 优质品牌商家
  • 2026年5月宠物空气净化器品牌推荐:五款排名产品评测夜用防毛发缠绕 - 品牌推荐