C++按值传递引发的对象损坏问题:请求复现示例
复现按值传递对象时因析构副作用破坏原对象的示例
Herbert Schildt提到的这种情况,核心原因是默认拷贝构造函数的浅拷贝行为。当类包含动态分配的资源(比如堆内存)时,默认拷贝只会复制指针本身,导致原对象和函数内的副本共享同一块内存。当函数执行完毕,副本的析构函数会释放这块共享内存,原对象的指针就会变成野指针,进而导致原对象被破坏,访问时触发未定义行为。
未定义拷贝构造函数的问题示例
#include <iostream> #include <cstring> class MyString { private: char* data; public: // 构造函数:动态分配内存存储字符串 MyString(const char* str) { data = new char[strlen(str) + 1]; std::cout << "[构造] 分配内存地址: " << static_cast<void*>(data) << "\n"; strcpy(data, str); } // 析构函数:释放动态分配的内存 ~MyString() { std::cout << "[析构] 释放内存地址: " << static_cast<void*>(data) << "\n"; delete[] data; data = nullptr; } // 打印当前对象的字符串内容和内存地址 void show() const { std::cout << "内容: " << (data ? data : "野指针(内存已释放)") << " | 内存地址: " << static_cast<void*>(data) << "\n"; } }; // 按值传递对象的函数 void process_string(MyString obj) { std::cout << "\n函数内的副本对象:\n"; obj.show(); } // 此处调用副本的析构函数,释放共享内存 int main() { MyString original("Hello C++"); std::cout << "\n调用函数前的原对象:\n"; original.show(); process_string(original); // 按值传递,触发默认拷贝构造 std::cout << "\n调用函数后的原对象:\n"; original.show(); // 原对象的内存已被副本析构释放,访问属于未定义行为 return 0; }
运行结果说明
- 程序启动后,原对象
original构造,分配一块内存存储字符串,打印内容和地址。 - 调用
process_string时,默认拷贝构造函数创建副本obj,obj的data指针和original指向同一块内存。 - 函数执行完毕,副本
obj析构,释放共享内存。 - 返回
main后,原对象original的data已经是野指针,此时访问它会输出乱码、“野指针”提示,甚至直接崩溃——这就是书中所说的原对象被修改/破坏的副作用。
解决方法:自定义拷贝构造函数(深拷贝)
为类添加自定义拷贝构造函数,实现深拷贝,让副本拥有独立的内存空间,这样副本析构时不会影响原对象:
// 在MyString类中添加拷贝构造函数 MyString(const MyString& other) { std::cout << "[拷贝构造] 为副本分配新内存\n"; data = new char[strlen(other.data) + 1]; strcpy(data, other.data); }
添加后,副本和原对象的data指向不同内存,析构副本只会释放自己的内存,原对象不受影响。
内容的提问来源于stack exchange,提问作者YoussefMabrouk
相关产品推荐
相关产品推荐

