Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/161.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 三的法则是什么? 复制对象意味着什么 什么是复制构造函数和复制赋值运算符 我什么时候需要自己申报 如何防止复制我的对象 导言_C++_Copy Constructor_Assignment Operator_C++ Faq_Rule Of Three - Fatal编程技术网

C++ 三的法则是什么? 复制对象意味着什么 什么是复制构造函数和复制赋值运算符 我什么时候需要自己申报 如何防止复制我的对象 导言

C++ 三的法则是什么? 复制对象意味着什么 什么是复制构造函数和复制赋值运算符 我什么时候需要自己申报 如何防止复制我的对象 导言,c++,copy-constructor,assignment-operator,c++-faq,rule-of-three,C++,Copy Constructor,Assignment Operator,C++ Faq,Rule Of Three,C++使用值语义处理用户定义类型的变量。 这意味着对象在各种上下文中被隐式复制, 我们应该理解“复制对象”的真正含义 让我们考虑一个简单的例子: class person { std::string name; int age; public: person(const std::string& name, int age) : name(name), age(age) { } }; int main() { person a(&qu

C++使用值语义处理用户定义类型的变量。 这意味着对象在各种上下文中被隐式复制, 我们应该理解“复制对象”的真正含义

让我们考虑一个简单的例子:

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?
}
(如果您对
姓名(姓名)、年龄(年龄)
部分感到困惑, 这称为a。)

特殊成员职能 复制
对象意味着什么?
main
功能显示两种不同的复制场景。 初始化
人员b(a)由复制构造函数执行。
它的工作是基于现有对象的状态构造一个新对象。
分配
b=a
由复制分配操作员执行。 它的工作一般比较复杂, 因为目标对象已经处于某种需要处理的有效状态

因为我们自己既没有声明复制构造函数,也没有声明赋值运算符(也没有声明析构函数), 这些都是为我们隐式定义的。引用标准:

[…]复制构造函数和复制赋值运算符[…]和析构函数是特殊的成员函数。 [注意:实现将隐式声明这些成员函数 对于某些类类型,当程序没有显式声明它们时。 如果使用它们,实现将隐式定义它们。[…]结束注释] [n3126.pdf第12节§1]

默认情况下,复制对象意味着复制其成员:

非联合类X的隐式定义的复制构造函数执行其子对象的成员复制。 [n3126.pdf第12.8节§16]

非联合类X的隐式定义的复制赋值运算符执行成员方式的复制赋值 它的子对象。 [n3126.pdf第12.8节§30]

隐式定义
person
的隐式定义的特殊成员函数如下所示:

// 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()
{
}
class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};
在这种情况下,成员复制正是我们想要的:
name
age
被复制,因此我们得到了一个独立的
person
对象。 隐式定义的析构函数始终为空。 在本例中,这也很好,因为我们没有在构造函数中获取任何资源。 成员的析构函数在
person
析构函数完成后隐式调用:

在执行析构函数的主体并销毁主体内分配的任何自动对象之后, 类X的析构函数调用X的直接[…]成员的析构函数 [n3126.pdf 12.4§6]

管理资源 那么,我们应该在什么时候明确声明这些特殊的成员函数呢? 当我们班管理一个资源时,就是, 当类的对象负责该资源时。 这通常意味着资源是在构造函数中获取的 (或传入构造函数)并在析构函数中释放

让我们回到标准的C++中。 没有所谓的
std::string
,程序员喜欢指针。
person
类可能如下所示:

// 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()
{
}
class person
{
    char* name;
    int age;

public:

    // the constructor acquires a resource:
    // in this case, dynamic memory obtained via new[]
    person(const char* the_name, int the_age)
    {
        name = new char[strlen(the_name) + 1];
        strcpy(name, the_name);
        age = the_age;
    }

    // the destructor must release this resource via delete[]
    ~person()
    {
        delete[] name;
    }
};
即使在今天,人们仍然以这种方式上课,并因此陷入困境: “我把一个人推到一个向量中,现在我得到了疯狂的记忆错误!” 请记住,默认情况下,复制对象意味着复制其成员, 但是复制
name
成员只是复制一个指针,而不是它指向的字符数组! 这有几个不愉快的影响:

  • 通过
    a
    进行的更改可以通过
    b
    进行观察
  • 一旦
    b
    被销毁,
    a.name
    就是一个悬空的指针
  • 如果
    a
    被销毁,则删除悬空指针会产生错误
  • 由于作业未考虑作业前所指的
    名称
    , 迟早你会发现到处都是内存泄漏
  • 明确定义 由于memberwise复制没有所需的效果,我们必须明确定义复制构造函数和复制赋值运算符,以便对字符数组进行深度复制:

    // 1. copy constructor
    person(const person& that)
    {
        name = new char[strlen(that.name) + 1];
        strcpy(name, that.name);
        age = that.age;
    }
    
    // 2. copy assignment operator
    person& operator=(const person& that)
    {
        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;
    }
    
    注意初始化和赋值之间的区别: 我们必须在分配给
    name
    之前删除旧状态,以防止内存泄漏。 此外,我们还必须防止表单
    x=x
    的自赋值。 如果没有该检查,
    delete[]name
    将删除包含源字符串的数组, 因为在编写
    x=x
    时,
    this->name
    that.name
    都包含相同的指针

    例外安全 不幸的是,如果
    newchar[…]
    由于内存耗尽而引发异常,此解决方案将失败。 一种可能的解决方案是引入局部变量并对语句重新排序:

    // 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
    ,而无需给出定义:

    private:
    
        person(const person& that);
        person& operator=(const person& that);
    
    或者,您可以继承自
    boost::noncopyable
    或将其声明为已删除(在C++11及更高版本中):

    三法则 有时,您需要实现一个管理资源的类。 (切勿在一个类中管理多个资源, 这只会导致疼痛。) 在这种情况下,请记住三个规则:

    如果需要显式声明
    class Car //A very simple class just to demonstrate what these definitions mean.
    //It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
    {
    private String sPrintColor;
    private String sModel;
    private String sMake;
    
    public changePaint(String newColor)
    {
       this.sPrintColor = newColor;
    }
    
    public Car(String model, String make, String color) //Constructor
    {
       this.sPrintColor = color;
       this.sModel = model;
       this.sMake = make;
    }
    
    public ~Car() //Destructor
    {
    //Because we did not create any custom types, we aren't adding more code.
    //Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
    //Since we did not use anything but strings, we have nothing additional to handle.
    //The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
    }
    
    public Car(const Car &other) // Copy Constructor
    {
       this.sPrintColor = other.sPrintColor;
       this.sModel = other.sModel;
       this.sMake = other.sMake;
    }
    public Car &operator =(const Car &other) // Assignment Operator
    {
       if(this != &other)
       {
          this.sPrintColor = other.sPrintColor;
          this.sModel = other.sModel;
          this.sMake = other.sMake;
       }
       return *this;
    }
    
    }
    
    Car car1 = new Car("mustang", "ford", "red");
    Car car2 = car1; //Call the copy constructor
    car2.changePaint("green");
    //car2 is now green but car1 is still red.
    
    //Shallow copy example
    //Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
    //Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.
    
     Car car1 = new Car("ford", "mustang", "red"); 
     Car car2 = car1; 
     car2.changePaint("green");//car1 is also now green 
     delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve 
     the address of where car2 exists and delete the memory...which is also
     the memory associated with your car.*/
     car1.changePaint("red");/*program will likely crash because this area is
     no longer allocated to the program.*/
    
        MyClass x(a, b);
        MyClass y(c, d);
        x = y; // This is a shallow copy if assignment operator is not provided
    
    // default constructor
    My_Class a;
    
    // copy constructor
    My_Class b(a);
    
    // copy constructor
    My_Class c = a;
    
    // copy assignment operator
    b = a;
    
    class MemoryBlock
    {
    public:
    
    //code here
    
    private:
    MemoryBlock(const MemoryBlock& other)
    {
       cout<<"copy constructor"<<endl;
    }
    
    // Copy assignment operator.
    MemoryBlock& operator=(const MemoryBlock& other)
    {
     return *this;
    }
    };
    
    int main()
    {
       MemoryBlock a;
       MemoryBlock b(a);
    }
    
    class MemoryBlock
    {
    public:
    MemoryBlock(const MemoryBlock& other) = delete
    
    // Copy assignment operator.
    MemoryBlock& operator=(const MemoryBlock& other) =delete
    };
    
    
    int main()
    {
       MemoryBlock a;
       MemoryBlock b(a);
    }