C++ 3/5规则

文章目录

  • 问题
    • 特殊的成员函数
      • 隐式的定义
  • 资源管理
    • 显示定义
    • 异常安全
    • 不可复制资源
  • 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是复制指针,而不是复制它指向的一片内存。这里会出现几个问题:

    1. a的变化可以观察到b的变化
    1. 一旦b销毁,a.name就是一个悬空指针
    1. 如果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);        // 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

你可能感兴趣的:(c++,开发语言)