[C++] 深拷贝和浅拷贝
c++的类中含有指针类型时,在进行拷贝时要注意深拷贝和浅拷贝的问题。
(1) 浅拷贝采用"位拷贝"的方式。对于基础类型,如int, float等,可以直接copy过来;
但是对于指针类型,在copy时要格外注意,浅拷贝只copy了指针指向的地址,并未复制创建该地址上的对象内容,容易引发内存泄露、多次析构等问题。
(2) 深拷贝则为新对象的成员重新开辟内存空间,每个对象共同拥有自己的资源,必须显式提供拷贝构造函数和赋值运算符。
1. 浅拷贝的问题
以类File为例,首先创建了File类型的一个对象a, File类型的对象b是由a 浅拷贝得到。
在浅拷贝时,对于a中的指针类型对象_fp,b只copy了a中_fp指针指向的对象的地址,
那么浅拷贝后,a, b对象的_fp指针都指向了同一块内存。
这在类进行析构时很容易出现被多次析构的问题。
#include <iostream> using namespace std; class File { public: explicit File(const char *file_name) {}; ~File() {}; void addr_fp() { cout << _fp << endl; } private: FILE *_fp; }; int main() { File a("./a.txt"); cout << "a._fp addr" << endl; a.addr_fp(); File b = a; cout << "b._fp addr" << endl; b.addr_fp(); return 0; } /** output a._fp addr 0x7ffee45649d8 b._fp addr 0x7ffee45649d8 **/
2. 解决方法
对于含有指针类型的类,可以采用进制拷贝构造、赋值构造等方式来避免(1)中的问题。(RAII没有复制行为)
下例中通过定义一个拷贝和赋值构造的宏,并在private中进行声明,
那么如果有企图对该类对象进行拷贝或赋值构造,会触发private 的constructor引发报错。
#include <iostream> using namespace std; #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&);\ TypeName& operator=(const TypeName&) class File { public: explicit File(const char *file_name) {}; ~File() {}; void addr_fp() { cout << _fp << endl; } private: FILE *_fp; DISALLOW_COPY_AND_ASSIGN(File); }; int main() { File a("./a.txt"); cout << "a._fp addr" << endl; a.addr_fp(); File b = a; cout << "b._fp addr" << endl; b.addr_fp(); return 0; } /** output deep_shallow_copy.cpp:30:11: error: calling a private constructor of class 'File' File b = a; ^ deep_shallow_copy.cpp:21:27: note: declared private here DISALLOW_COPY_AND_ASSIGN(File); ^ 1 error generated. **/