C++:拷贝构造函数和赋值运算符重载
拷贝构造函数和赋值运算符重载
拷贝构造函数
对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=88;
int b=a;
而对于类对象的拷贝,则相对复杂,请看下面的例子:
#include <iostream>
using namespace std;
class copyDemoClass {
public:
copyDemoClass(int i):_i_ptr(new int(i)) {
cout << "Construct..." << endl;
}
~copyDemoClass() { delete _i_ptr; }
void displayValue() const {
cout << "Value is " << *_i_ptr << endl;
}
private:
int* _i_ptr;
};
int main(int agrc, char* argv[]) {
copyDemoClass demo_class1(1);
demo_class1.displayValue();
copyDemoClass demo_class2(demo_class1);
demo_class2.displayValue();
return 0;
}
执行上述代码后结果如下:
出现double free的结果是因为用demo_class1拷贝demo_class2,在程序员没有实现拷贝构造函数的情况下会进行按位拷贝。demo_class1和demo_class2的_i_ptr`成员变量会指向同一块内存,在main函数执行完成之后分别对两个类对象进行析构,就会对这块内存进行两次释放。上述的拷贝方式被称为“浅拷贝”。
出现上述问题的最佳解决方法是程序员自己实现一个拷贝构造函数来完成“深拷贝”。请看下述代码:
#include <iostream>
using namespace std;
class copyDemoClass {
public:
copyDemoClass(int i):_i_ptr(new int(i)) {
cout << "Construct..." << endl;
}
copyDemoClass(const copyDemoClass& other):_i_ptr(new int(*other._i_ptr)) {
cout << "Copy Construct..." << endl;
}
~copyDemoClass() { delete _i_ptr; }
void displayValue() const {
cout << "Value is " << *_i_ptr << endl;
}
private:
int* _i_ptr;
};
int main(int agrc, char* argv[]) {
copyDemoClass demo_class1(1);
demo_class1.displayValue();
copyDemoClass demo_class2(demo_class1);
demo_class2.displayValue();
return 0;
}
上述代码第7行实现了一个拷贝构造函数,在执行copyDemoClass demo_class2(demo_class1) 时会调用该拷贝构造函数,会为demo_class2的 _i_ptr 成员分配一段新的内存,在执行析构函数时会释放该段内存,而不是释放demo_class1的成员变量指向的地址,不会造成二次释放。该段代码执行结果如下:
对此建议对于有复制意义的类必须显式地给出复制构造函数。
赋值运算符重载
对于有复制赋值意义的类,除了拷贝构造函数外,还需要手动重载赋值运算符。请看如下代码:
#include <iostream>
using namespace std;
class copyDemoClass {
public:
copyDemoClass(int i):_i_ptr(new int(i)) {
cout << "Construct..." << endl;
}
copyDemoClass(const copyDemoClass& other):_i_ptr(new int(*other._i_ptr)) {
cout << "Copy Construct..." << endl;
}
~copyDemoClass() { delete _i_ptr; }
void displayValue() const {
cout << "Value is " << *_i_ptr << endl;
}
private:
int* _i_ptr;
};
int main(int agrc, char* argv[]) {
copyDemoClass demo_class1(1);
demo_class1.displayValue();
copyDemoClass demo_class2(-1);
demo_class2 = demo_class1;
demo_class2.displayValue();
return 0;
}
执行上述代码后结果如下:
发现熟悉的double free又出现了,但这时我们已经实现了拷贝构造函数,为什么还会出现double free的现象呢?
在上述代码中,首先会用参数-1作为demo_class2的构造函数参数,在demo_class2构造完成之后才调用的‘=’操作符,而此时demo_class2已经构造完成,所以不会调用任何构造函数(包括拷贝构造函数),而是调用编译器生成的默认的赋值运算符,而默认的赋值运算符是进行按位赋值的浅拷贝操作,所以在调用析构函数的时候demo_class的指针成员变量会进行两次释放的操作。
将上述代码修改如下:
#include <iostream>
using namespace std;
class copyDemoClass {
public:
copyDemoClass(int i):_i_ptr(new int(i)) {
cout << "Construct..." << endl;
}
copyDemoClass(const copyDemoClass& other):_i_ptr(new int(*other._i_ptr)) {
cout << "Copy Construct..." << endl;
}
copyDemoClass& operator=(const copyDemoClass& other) {
cout << "Assign Operator..." << endl;
if(this == &other) {
return *this;
}
_i_ptr = new int(*other._i_ptr);
return *this;
}
~copyDemoClass() { delete _i_ptr; }
void displayValue() const {
cout << "Value is " << *_i_ptr << endl;
}
private:
int* _i_ptr;
};
int main(int agrc, char* argv[]) {
copyDemoClass demo_class1(1);
demo_class1.displayValue();
copyDemoClass demo_class2(-1);
demo_class2 = demo_class1;
demo_class2.displayValue();
return 0;
}
上述代码执行结果如下: