C++笔记(5)浅拷贝和深拷贝
1. 定义
浅拷贝(shallow copy):多个对象共用同一块资源,同一块资源释放多次,崩溃或者内存泄漏
深拷贝(deep copy):每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。
2. 示例
(书p355)
#include <iostream> using namespace std; //类声明 class String { private: char* str; int len; static int num_strings; public: String(const char* s); String(); ~String(); friend std::ostream& operator<<(std::ostream& os, const String& s); }; //不能在类声明中初始化静态成员变量 int String::num_strings = 0; String::String(const char* s) { len = strlen(s); str = new char[len + 1]; strcpy_s(str, strlen(s) + 1, s); num_strings++; cout << "num_strings:\"" << str << "\"object created\n"; } //类实现 String::String() { len = 4; str = new char[len + 1]; strcpy_s(str, 4, "c++"); num_strings++; cout << "num_strings:\"" << str << "\"object created\n"; } String::~String() { cout << "num_strings:\"" << str << "\"object deleted\n"; num_strings--; cout << num_strings << " object left\n"; delete[] str; } std::ostream& operator<<(std::ostream& os, const String& s) { os << s.str << ":" << int(s.str) << endl; return os; } //主函数 int main() { { String s1 = "aaaa"; cout << s1; String s2 = s1; cout << s2; } cout << "End of main()!\n"; return 0; }
运行中断:
num_strings:"aaaa"object created aaaa:16857352 aaaa:16857352 num_strings:"aaaa"object deleted 0 object left num_strings:"葺葺葺葺葺葺葺葺?b@?"object deleted -1 object left
默认的拷贝构造函数逐个复制非静态成员(浅拷贝),复制的是成员的值。
String s2 = s1;
相当于:
String s2; s2.str = s1.str; s2.len=s1.len;
其中,s2.str = s1.str复制的不是字符串,而是指向字符串的指针。也就是说,将s2初始化为是s1后,得到两个指针指向一个字符串。析构函数释放了s2.str指针指向的内存,因此在释放s2时,导致了不确定,可能有害的结果。
解决这类设计中这种问题的方法是进行深拷贝(deep copy)。也就是说,拷贝构造函数应该复制字符串并将副本的地址赋给str成员,而不仅仅是复制字符串地址。这样每个对象都有自己的字符串,而不会试图去释放已经被释放的字符串。
可以定义一个显式拷贝构造函数以解决问题:
String::String(const String& st) { len = st.len; str = new char[len + 1]; strcpy_s(str, len+1, st.str); num_strings++; cout << "num_strings:\"" << str << "\"object created\n"; }
运行结果:
num_strings:"aaaa"object created num_strings:"aaaa"object created num_strings:"aaaa"object deleted 1 object left num_strings:"aaaa"object deleted 0 object left
End of main()!
3. 总结
如果类中包含了使用new初始化的指针成员,应当定义一个拷贝构造函数,以复制指向的数据,而不是指针。