拷贝构造函数接受一个const引用同类型的对象作为参数,将其每一个参数拷贝给需要初始化的对象;
拷贝赋值运算符重载=号,接受一个同类型的const引用对象,返回一个该类型的引用。
#include <string>
class HasPtr {
public:
HasPtr(const std::string &s = std::string()) // 构造函数
: ps(new std::string()), i(0) {}
HasPtr(const HasPtr &obj) // 拷贝构造函数,注意参数是常量引用类型
: ps(new std::string(*obj.ps)), i(obj.i) {}
HasPtr & operator=(const HasPtr &rhs) { // 拷贝赋值函数,参数是常量引用,返回值是引用
ps = new std::string(*rhs.ps); // 直接拷贝rhs的ps是致命错误
i = rhs.i;
}
~HasPtr() {
delete ps;
}
private:
std::string *ps;
int i;
};
struct X {
X() { std::cout << "X()" << std::endl; }
X(const X &x) { std::cout << "X(const X&)" << std::endl; }
X &operator=(const X &x) {
std::cout << "X=" << std::endl;
return *this;
}
~X() { std::cout << "~X()" << std::endl; }
};
int main() {
X a;
X b(a);
X c;
c = a;
}
问题是,改变c和a的赋值方式:
X c = a;
输出变成:
不再使用拷贝赋值运算符,而是使用拷贝构造函数。
个人理解是,此处执行过程不是先默认构造c,然后调用拷贝赋值运算符,而是直接优化成一次拷贝构造。
如果一个类需要析构函数,那么几乎可以肯定它也需要拷贝构造和拷贝赋值运算符函数。
例如类中的一个数据成员是new出来的指针,如果使用默认的拷贝构造和拷贝赋值,指针会被复制,当原始对象析构时,指针指向的对象被delete,而新对象在析构时,会重复delete这片内存,会发生未定义行为。或者存在内存已经被释放,但是还有其他对象含有指向这一内存的指针,读取会发生错误。
如果一个类需要拷贝构造函数,则它一定需要拷贝复制运算符,反之亦然。但它不一定需要析构函数。