class person
{
std::string name;
int age;
public:
person(const std::string& name, int age) : name(name), age(age)
{
}
};
int main()
{
person a("Bjarne Stroustrup", 60);
person b(a); // What happens here?
b = a; // And here?
}
// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}
// 2. copy assignment operator
person& operator=(const person& that)
{
name = that.name;
age = that.age;
return *this;
}
// 3. destructor
~person()
{
}
在这种情况下,成员复制正是我们想要的:复制姓名和年龄,因此我们得到一个独立的、独立的 person 对象。隐式定义的析构函数始终为空。在这种情况下这也很好,因为我们没有在构造函数中获取任何资源。在 person 析构函数完成后,隐式调用成员的析构函数:
class person
{
public:
person(const char* the_name, int the_age)
{
name = new char[strlen(the_name)+1];
strcpy(name, the_name);
age = the_age;
}
~person()
{
delete[] name;
}
private:
char *name;
int age;
}
默认情况下: 拷贝对象意味着拷贝它的成员,但是name在这里是一个指针,复制name是复制指针,而不是复制它指向的一片内存。这里会出现几个问题:
//1. copy construct
person(const person &that)
{
name = new char[strlen(that.name)+1];
strcpy(name ,that.name);
age = that.age;
}
// 2.copy assignment to operator
person& operator=(const person&htat)
{
if(this != &that)
{
delete[] name;
// This is a dangerous point in the flow of execution!
// We have temporarily invalidated the class invariants,
// and the next statement might throw an exception,
// leaving the object in an invalid state :(
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
return *this;
}
不幸的是,如果 new char[…] 由于内存耗尽而引发异常,则此解决方案将失败。一种可能的解决方案是引入局部变量并对语句重新排序:
// 2. copy assignment operator
person& operator=(const person& that)
{
char* local_name = new char[strlen(that.name) + 1];
// If the above statement throws,
// the object is still in the same state as before.
// None of the following statements will throw an exception :)
strcpy(local_name, that.name);
delete[] name;
name = local_name;
age = that.age;
return *this;
}
有些资源不能或不应该被复制,例如文件句柄或互斥锁。在这种情况下,只需将复制构造函数和复制赋值运算符声明为私有,而不给出定义:
private:
person(const person& that);
person& operator=(const person& that);
或者,您可以继承 boost::noncopyable 或将它们声明为已删除(在 C++11 及更高版本中)
person(const person& that) = delete;
person& operator=(const person& that) = delete;
有时您需要实现一个管理资源的类(或者带有指针的类,可能意味着就需要管理资源)。 (永远不要在一个类中管理多个资源,这只会带来痛苦。)在这种情况下,请记住三条规则:
从 C++11 开始,对象有 2 个额外的特殊成员函数:移动构造函数和移动赋值。
class person
{
std::string name;
int age;
public:
person(const std::string& name, int age); // Ctor
person(const person &) = default; // 1/5: Copy Ctor
person(person &&) noexcept = default; // 4/5: Move Ctor
person& operator=(const person &) = default; // 2/5: Copy Assignment
person& operator=(person &&) noexcept = default; // 5/5: Move Assignment
~person() noexcept = default; // 3/5: Dtor
};
What is the Rule of Three