C++类和对象:面向对象编程的核心(二)
之前我们总结了C类和对象的概念也提到了构造函数与析构函数本文我们来了解拷贝构造函数与赋值运算符重载一、拷贝构造函数拷贝构造函数是一种特殊构造函数使用同一个类的已有对象去创建新对象。特点1、函数名与类名相同2、唯一形参同类的 const 引用必须是引用直接传值会无限递归调用3、没有返回值4、如果自己不写编译器会自动提供默认拷贝构造函数。一默认拷贝构造函数浅拷贝逐成员拷贝值拷贝 / 浅拷贝对于普通成员变量直接复制指针成员只复制变量本身的值地址不会新开堆内存。问题根源两个对象指针指向同一块堆内存析构重复释放。二自写拷贝构造函数深拷贝重新开辟堆内存让新对象拥有独立资源解决浅拷贝崩溃问题三什么时候调用拷贝构造函数拷贝构造函数在以下几种情况下会被调用用一个对象初始化另一个对象时class MyClass { public: MyClass(const MyClass other) { // 拷贝构造函数实现 } }; int main() { MyClass obj1; MyClass obj2 obj1; // 调用拷贝构造函数 MyClass obj3(obj1); // 调用拷贝构造函数 return 0; }函数参数传递对象时按值传递void processObject(MyClass obj) { // 函数体 } int main() { MyClass original; processObject(original); // 调用拷贝构造函数创建形参obj return 0; }函数返回对象时按值返回MyClass createObject() { MyClass obj; return obj; // 可能会调用拷贝构造函数取决于编译器优化 } int main() { MyClass newObj createObject(); // 调用拷贝构造函数 return 0; }容器中插入对象时#include vector int main() { std::vectorMyClass vec; MyClass obj; vec.push_back(obj); // 调用拷贝构造函数 return 0; }注意现代编译器会进行返回值优化RVO和命名返回值优化NRVO在某些情况下会避免不必要的拷贝构造函数调用提高程序效率。二、赋值运算符重载赋值运算符重载是C中允许我们自定义类对象之间赋值行为的运算符重载。与拷贝构造函数不同赋值运算符用于已存在对象之间的赋值操作。特点函数名固定为operator返回类型通常是类的引用支持连续赋值参数通常是同类的 const 引用需要处理自赋值情况需要释放原有资源并分配新资源深拷贝一默认赋值运算符浅拷贝如果不自定义赋值运算符编译器会生成默认的赋值运算符执行逐成员浅拷贝class ShallowCopyClass { private: int* data; int size; public: // 构造函数 ShallowCopyClass(int s) : size(s) { data new int[size]; for (int i 0; i size; i) { data[i] i; } } // 析构函数 ~ShallowCopyClass() { delete[] data; } // 没有自定义赋值运算符使用默认浅拷贝 }; int main() { ShallowCopyClass obj1(5); ShallowCopyClass obj2(3); obj2 obj1; // 默认赋值运算符浅拷贝 // obj1和obj2的data指针指向同一内存 return 0; // 程序结束时会双重释放导致崩溃 }问题与默认拷贝构造函数类似默认赋值运算符只复制指针值导致两个对象指向同一块堆内存析构时重复释放。二自定义赋值运算符深拷贝为了解决浅拷贝问题我们需要自定义赋值运算符实现深拷贝class DeepCopyClass { private: int* data; int size; public: // 构造函数 DeepCopyClass(int s) : size(s) { data new int[size]; for (int i 0; i size; i) { data[i] i; } } // 拷贝构造函数深拷贝 DeepCopyClass(const DeepCopyClass other) : size(other.size) { data new int[size]; for (int i 0; i size; i) { data[i] other.data[i]; } } // 赋值运算符重载深拷贝 DeepCopyClass operator(const DeepCopyClass other) { // 1. 检查自赋值 if (this other) { return *this; } // 2. 释放原有资源 delete[] data; // 3. 分配新资源并复制数据 size other.size; data new int[size]; for (int i 0; i size; i) { data[i] other.data[i]; } // 4. 返回当前对象的引用 return *this; } // 析构函数 ~DeepCopyClass() { delete[] data; } // 打印数据用于测试 void print() const { for (int i 0; i size; i) { std::cout data[i] ; } std::cout std::endl; } }; int main() { DeepCopyClass obj1(5); DeepCopyClass obj2(3); std::cout 赋值前obj2: ; obj2.print(); // 输出: 0 1 2 obj2 obj1; // 调用自定义赋值运算符深拷贝 std::cout 赋值后obj2: ; obj2.print(); // 输出: 0 1 2 3 4 // 修改obj1不会影响obj2 // ...假设有修改方法 return 0; // 正常析构不会崩溃 }三拷贝构造函数 vs 赋值运算符对比项拷贝构造函数赋值运算符调用时机创建新对象时已存在对象之间赋值时函数签名ClassName(const ClassName)ClassName operator(const ClassName)返回类型无构造函数类的引用支持连续赋值资源管理分配新资源先释放旧资源再分配新资源自处理不需要新对象需要检查自赋值示例MyClass obj2 obj1;obj2 obj1;四现代C的改进拷贝并交换惯用法使用拷贝并交换惯用法可以简化赋值运算符的实现同时提供强异常安全性class SmartClass { private: int* data; int size; // 交换函数 friend void swap(SmartClass first, SmartClass second) noexcept { using std::swap; swap(first.data, second.data); swap(first.size, second.size); } public: // 构造函数 SmartClass(int s 0) : size(s), data(size ? new int[size] : nullptr) { for (int i 0; i size; i) { data[i] i; } } // 拷贝构造函数 SmartClass(const SmartClass other) : size(other.size), data(size ? new int[size] : nullptr) { for (int i 0; i size; i) { data[i] other.data[i]; } } // 赋值运算符拷贝并交换 SmartClass operator(SmartClass other) { // 注意按值传递 swap(*this, other); // 交换当前对象和临时对象 return *this; // other离开作用域时会释放旧资源 } // 移动构造函数C11 SmartClass(SmartClass other) noexcept : data(nullptr), size(0) { swap(*this, other); } // 移动赋值运算符C11 SmartClass operator(SmartClass other) noexcept { swap(*this, other); return *this; } // 析构函数 ~SmartClass() { delete[] data; } }; int main() { SmartClass obj1(5); SmartClass obj2; obj2 obj1; // 调用拷贝并交换的赋值运算符 return 0; }优点代码简洁避免重复自动处理自赋值交换是安全的提供强异常安全性同时支持拷贝和移动语义C11五什么时候需要自定义赋值运算符类包含动态分配的内存指针成员类包含文件句柄、网络连接等资源需要实现引用计数或写时复制需要特殊的赋值逻辑如数据库记录复制总结赋值运算符重载是C资源管理的重要组成部分。对于管理资源的类通常需要同时实现拷贝构造函数、赋值运算符、析构函数Rule of Three在C11及以后还需要考虑移动构造函数、移动赋值运算符Rule of Five。