C++ 类的核心:默认成员函数之构造、析构与拷贝构造全解析 前言在 C 中当我们定义一个空类时它真的 空 吗答案是否定的。编译器会默默为我们生成多个默认成员函数它们掌管着对象从创建、复制到销毁的完整生命周期。理解这些默认函数的行为是掌握 C 面向对象编程的关键一步。本文将深入讲解类的默认成员函数体系并重点剖析构造函数、析构函数和拷贝构造函数的底层原理与使用细节。一、类的默认成员函数概览1.1 什么是默认成员函数如果我们在类中不显式定义某些函数编译器会自动生成对应的默认版本。这些由编译器自动生成的函数就叫做默认成员函数。C 类有 6 个经典的默认成员函数构造函数对象创建时初始化析构函数对象销毁时清理资源拷贝构造函数用已有对象创建新对象赋值运算符重载对象之间的赋值取地址运算符重载取普通对象地址const 取地址运算符重载取 const 对象地址其中前四个是重中之重也是面试高频考点。后两个几乎不需要我们手动实现编译器默认生成的版本完全够用。1.2 一个 空类 的真实大小空类大小为 1 字节这是因为编译器需要给每个对象分配唯一的内存地址来区分不同对象。虽然没有成员变量但 6 个默认成员函数已经隐含其中。二、构造函数2.1 概念与作用构造函数是一个特殊的成员函数名字与类名相同没有返回值连 void 都不写在对象创建时由编译器自动调用且在对象整个生命周期内只调用一次。它的核心任务是初始化对象的成员变量而不是开空间创建对象对象的空间在构造函数调用前已经分配好了。2.2 特性函数名与类名相同无返回值对象实例化时编译器自动调用可以重载一个类可以有多个构造函数如果类中没有显式定义构造函数编译器会自动生成一个无参的默认构造函数一旦显式定义编译器便不再生成2.3 代码示例2.4 默认构造函数默认构造函数指的是不用传参就可以调用的构造函数有三种编译器自动生成的无参构造我们显式写的无参构造全缺省的构造函数⚠️ 注意默认构造函数只能有一个。无参构造和全缺省构造不能同时存在否则会产生二义性。2.5 编译器生成的默认构造做了什么很多人以为默认构造会把成员变量初始化为 0这是错误的。C 把类型分为两类内置类型基本类型int、char、double、指针等自定义类型class、struct 定义的类型编译器生成的默认构造函数规则对内置类型成员不做处理值是随机的对自定义类型成员会去调用它的默认构造函数这也是 C 经常被吐槽的点 —— 内置类型成员不初始化。所以一般建议我们自己写构造函数确保成员变量都有合理初值。三、析构函数3.1 概念与作用析构函数与构造函数功能相反名字是类名前加同样没有返回值和参数。它在对象销毁时由编译器自动调用负责完成对象中资源的清理工作。3.2 特性析构函数名是在类名前加~无参数、无返回值一个类有且只有一个析构函数不能重载对象生命周期结束时编译器自动调用若未显式定义编译器会生成默认析构函数3.3 代码示例3.4 什么时候需要自己写析构如果类中没有申请资源堆内存、文件句柄等析构函数可以不写用编译器生成的默认即可如果类中有动态申请的资源必须显式写析构函数释放资源否则会造成内存泄漏3.5 编译器生成的默认析构做了什么和构造函数规则类似对内置类型成员不做处理对自定义类型成员会去调用它的析构函数四、拷贝构造函数4.1 概念拷贝构造函数是一个特殊的构造函数它只有一个形参且形参是本类对象的 const 引用。在用已有对象创建新对象时编译器会自动调用拷贝构造函数。4.2 为什么必须是引用这是初学者最容易问的问题。拷贝构造函数的参数必须是引用否则会引发无穷递归调用。如果用传值方式实参传给形参时又需要调用拷贝构造函数来创建形参 d这样就会无限递归下去。所以必须用引用传参一般加上 const 保证不修改原对象。4.3 正确写法4.4 编译器生成的默认拷贝构造如果我们不写编译器会自动生成默认拷贝构造函数它会进行浅拷贝按内存字节序直接拷贝。对于像 Date 这样没有动态资源的类默认拷贝构造完全够用。但对于有动态资源的类比如 Stack浅拷贝会出大问题。4.5 浅拷贝的问题这段代码会崩溃因为 s1 和 s2 的_data指向同一块内存两个对象销毁时都会调用析构函数释放同一块空间造成二次释放。4.6 深拷贝解决方案当类中涉及资源申请时必须自己实现拷贝构造做深拷贝这样每个对象都有自己独立的内存空间析构时互不影响。4.7 拷贝构造的调用场景使用已有对象创建新对象函数参数为类类型的传值调用函数返回值为类类型的传值返回五、总结函数调用时机是否有参是否可重载不写时编译器行为构造函数对象创建时可有参可无参是生成默认构造内置类型不处理自定义类型调用其构造析构函数对象销毁时无参否生成默认析构内置类型不处理自定义类型调用其析构拷贝构造用同类型对象初始化新对象时一个const 引用否生成默认拷贝执行浅拷贝逐字节复制核心记忆点1. **构造函数**管出生负责初始化**析构函数**管死亡负责清理资源2. **拷贝构造**是特殊的构造函数参数必须是引用否则无限递归3. 没有资源的类可以用编译器默认函数涉及动态内存、文件句柄等资源时必须自己实现深拷贝和析构4. 编译器生成的默认函数对内置类型 摆烂 不处理对自定义类型会递归调用对应函数掌握了这三大默认成员函数你就理解了 C 对象生命周期管理的核心。在此基础上再去学习赋值运算符重载和移动语义就能形成完整的知识体系。