2026/2/17 10:55:32
网站建设
项目流程
河南网站推广,怎么宣传,最好的seo优化公司,网站建设没有业务怎么办#x1f504; C 赋值运算符重载#xff1a;深拷贝 vs 浅拷贝的生死线#xff01;大家好#xff01;今天我们来聊一个 C 中极易被忽视、却可能引发严重 bug 的知识点——赋值运算符 operator 的重载。你可能写过 a b#xff0c;但当你的类中包含指向堆内存的指针时#x… C 赋值运算符重载深拷贝 vs 浅拷贝的生死线大家好今天我们来聊一个 C 中极易被忽视、却可能引发严重 bug 的知识点——赋值运算符operator的重载。你可能写过a b但当你的类中包含指向堆内存的指针时这个看似简单的等号就可能让你的程序崩溃、内存泄漏甚至“神秘地”修改不该改的数据别慌今天我们就用一段经典示例彻底搞懂为什么需要重载赋值运算符以及如何正确实现深拷贝。 编译器默认给你的 4 个函数在 C 中即使你什么都没写编译器也会悄悄为你的类生成以下 4 个函数默认构造函数无参空实现默认析构函数无参空实现默认拷贝构造函数逐成员值拷贝**默认赋值运算符operator**也是逐成员值拷贝⚠️ 问题来了“值拷贝”对指针来说就是“浅拷贝” 浅拷贝的灾难多个对象共用一块堆内存来看你写的Person类代码原样保留未作任何修改class Person { public: Person(int age) { // 将年龄数据开辟到堆区 m_Age new int(age); } // 重载赋值运算符 Person operator(Person p) { if (m_Age ! NULL) { delete m_Age; m_Age NULL; } // 编译器提供的代码是浅拷贝 // m_Age p.m_Age; // 提供深拷贝 解决浅拷贝的问题 m_Age new int(*p.m_Age); // 返回自身 return *this; } ~Person() { if (m_Age ! NULL) { delete m_Age; m_Age NULL; } } // 年龄的指针 int *m_Age; };如果不重载operator会发生什么假设使用默认赋值p2 p1; // 默认m_Age p1.m_Age 浅拷贝结果p1.m_Age和p2.m_Age指向同一块堆内存当p1或p2析构时delete这块内存另一个对象再访问或析构 →野指针 / 重复释放 → 程序崩溃这就是典型的浅拷贝陷阱。✅ 正确做法手动实现深拷贝你的重载版本完美解决了这个问题Person operator(Person p) { if (m_Age ! NULL) { delete m_Age; m_Age NULL; } m_Age new int(*p.m_Age); // 深拷贝新开内存复制值 return *this; }关键步骤先释放自身原有堆内存防止内存泄漏从源对象的堆数据中读取值重新 new 一块新内存返回*this的引用支持链式赋值如p3 p2 p1 测试效果void test01() { Person p1(18); Person p2(20); Person p3(30); p3 p2 p1; // 链式赋值 cout p1的年龄为 *p1.m_Age endl; cout p2的年龄为 *p2.m_Age endl; cout p3的年龄为 *p3.m_Age endl; }输出p1的年龄为18 p2的年龄为18 p3的年龄为18✅ 三个对象各自拥有独立的堆内存互不影响✅ 支持p3 p2 p1链式赋值因为返回了*this引用 黄金法则三/五法则Rule of Three/Five如果你的类中使用了动态内存如new或管理了其他资源文件句柄、socket 等那么你很可能需要同时自定义析构函数拷贝构造函数赋值运算符这就是著名的“三法则”C11 后扩展为“五法则”加上移动构造和移动赋值否则默认的浅拷贝会让你陷入万劫不复的调试深渊✅ 总结编译器自动生成的operator是浅拷贝对指针极其危险。当类中有堆区指针时必须重载赋值运算符实现深拷贝。记得先释放旧资源再分配新资源最后返回*this。支持链式赋值的关键返回引用如果你觉得这篇推文帮你避开了一个大坑欢迎点赞、收藏、转发也欢迎留言“你在项目中遇到过浅拷贝导致的 bug 吗”