文章目录
- 问题
- 特殊的成员函数
- 隐式的定义
- 资源管理
- 显示定义
- 异常安全
- 不可复制资源
- rule of three
- rule of five
- 参考
问题
- 拷贝对象意味着什么
- 拷贝构造和赋值拷贝有什么区别
- 如何声明拷贝构造和赋值拷贝
- 如何防止对象被拷贝
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是复制指针,而不是复制它指向的一片内存。这里会出现几个问题:
-
- a的变化可以观察到b的变化
-
- 一旦b销毁,a.name就是一个悬空指针
-
- 如果a销毁,删除悬空指针会出现意想不到的结果
- 4.由于赋值没有考虑赋值之前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;
rule of three
有时您需要实现一个管理资源的类(或者带有指针的类,可能意味着就需要管理资源)。 (永远不要在一个类中管理多个资源,这只会带来痛苦。)在这种情况下,请记住三条规则:
- 显示声明 :析构函数
- 显示声明 :拷贝构造函数
- 显示声明 :复制赋值运算符
rule of five
从 C++11 开始,对象有 2 个额外的特殊成员函数:移动构造函数和移动赋值。
class person
{std::string name;int age;public:person(const std::string& name, int age); // Ctorperson(const person &) = default; // 1/5: Copy Ctorperson(person &&) noexcept = default; // 4/5: Move Ctorperson& operator=(const person &) = default; // 2/5: Copy Assignmentperson& operator=(person &&) noexcept = default; // 5/5: Move Assignment~person() noexcept = default; // 3/5: Dtor
};
参考
What is the Rule of Three