包含动态内存分配的类需要自定义拷贝构造函数和赋值构造函数。即当类中有指针类型的成员变量时,一定要重写拷贝构造函数和赋值运算符,不要使用默认的。
无定义默认为浅拷贝,此时参数对象和创建对象(赋值对象)的指针成员指向同一块内存,调用二者的析构函数时第一对象调用释放内存成功,第二个对象会再次进行释放内存,此时运行时出错double free
故:应定义,且为深拷贝
二者区别是:
(1)拷贝构造函数生成新的类对象,而赋值运算符不能
(2)由于拷贝构造函数是直接构造一个新的类对象,所以在初始化这个对象之前不用检验源对象是否和新建对象相同。而赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中有内存分配要先释放掉
1 #include<iostream> 2 #include<string.h> 3 4 class A 5 { 6 public: 7 A() 8 { 9 std::cout << "constructor is called" << std::endl; 10 n = new int[10]; 11 n[0] = 1; 12 } 13 // 默认拷贝构造函数 浅拷贝 14 /*A(const A &t) // 拷贝构造函数的参数一定是引用,否则会出现无限递归 15 { 16 std::cout << "copy constructor is called" << std::endl; 17 n = t.n 18 }*/ 19 20 // 拷贝构造函数 深拷贝 21 A(const A &t) 22 { 23 std::cout << "copy constructor is called" << std::endl; 24 n = new int[10]; 25 memcpy(n, t.n, 10); // 通过按字节拷贝,将堆中一块内存存储到另一块内存 26 } 27 // 默认赋值运算符 浅拷贝 28 /*A &operator=(const A &t) // 形参和返回值一定要是引用类型,否则传参和返回时都会自动调用拷贝构造函数,多两次函数调用,效率低 29 { 30 std::cout << "assign constructor is called" << std::endl; 31 return *this; 32 if (n != NULL) 33 { 34 delete n; 35 n = NULL; 36 } 37 n = t.n; 38 return *this; 39 }*/ 40 // 深拷贝 41 A &operator=(const A &t) 42 { 43 std::cout << "assign constructor is called" << std::endl; 44 if (this == &t) 45 return *this; 46 if (n != NULL) 47 { 48 delete n; 49 n = NULL; 50 } 51 n = new int[10]; 52 memcpy(n, t.n, 10); //通过按字节拷贝,将堆中一块内存存储到另一块内存 53 return *this; 54 } 55 56 ~A() 57 { 58 std::cout << "destructor is called" << std::endl; 59 delete n; 60 n = NULL; 61 } 62 63 void get() { std::cout << "n[0]: " << n[0] << std::endl; } 64 private: 65 int *n; 66 }; 67 68 int main(void) 69 { 70 A a; 71 A b(a); 72 A c; 73 b = a; 74 b.get(); 75 c.get(); 76 77 return 0; 78 }
编译运行结果: