三个规则是什么?

发布于 2025-02-10 07:29:21 字数 132 浏览 3 评论 0原文

  • 复制对象是什么意思?
  • 复制构造函数复制分配操作员是什么?
  • 我什么时候需要自己声明它们?
  • 如何防止我的物体被复制?
  • What does copying an object mean?
  • What are the copy constructor and the copy assignment operator?
  • When do I need to declare them myself?
  • How can I prevent my objects from being copied?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

风启觞 2025-02-17 07:29:21

简介

C ++使用 value语义的用户定义类型的变量
这意味着对象在各种情况下隐式复制
我们应该了解“复制对象”的实际含义。

让我们考虑一个简单的示例

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 成员初始化器列表。)

特殊成员函数

复制person目的?
MAIN函数显示了两个不同的复制方案。
初始化Person B(a);复制构造函数执行。
它的工作是根据现有对象的状态构建新的对象。
分配b = a复制分配操作员执行。
它的工作通常更复杂
因为目标对象已经处于需要处理的某种有效状态。

由于我们既没有自己宣布复制构造函数,也不宣布我们自己的任务操作员(也不是毁灭者)
这些是为我们隐含定义的。标准的报价:

[...]复制构造函数和复制分配运算符,[...]和Destructor是特殊的成员函数。
[ Note 实施将隐式声明这些成员功能
对于某些课程类型时,该程序未明确声明它们。

如果使用使用,则实现将隐式定义它们。 [...] end Note ]
[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()
{
}

在这种情况下,成员的复制正是我们想要的:
名称age被复制,因此我们获得了一个独立的独立Person对象。
隐式定义的破坏者始终是空的。
在这种情况下,这也很好,因为我们没有在构造函数中获得任何资源。
Person destructor完成后,将成员的破坏者隐式调用:

执行破坏者的身体并破坏了身体内部分配的任何自动对象,
X类的破坏者称呼X的直接[...]成员
[N3126.pdf12.4§6]

管理资源

,那么我们何时应该明确声明这些特殊会员功能?
当我们的班级管理资源时,也就是说
当该类的对象是该资源的负责时。
这通常意味着资源是在构造函数中获得的
(或传递到构造函数中)和在破坏者中释放

让我们及时回去预标准C ++。
没有std :: string这样的东西,程序员爱上了指针。
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;
    }
};

即使在今天,人们仍然以这种风格写上课并陷入困境:
我把一个人推入矢量,现在我遇到了疯狂的记忆错误!
请记住,默认情况下,复制对象意味着复制其成员,
但是复制名称成员仅复制指针,而不是指向的字符数组!
这有几种不愉快的效果:

  1. 可以通过a进行更改,可以通过b观察。
  2. 一旦B被销毁,A.Name是一个悬空的指针。
  3. 如果a被销毁,则删除悬挂的指针产生 Undefined行为
  4. 由于分配没有考虑到分配之前指向的name
    迟早,您将在整个地方获得内存泄漏。

明确的定义

由于成员的复制没有所需的效果,因此我们必须明确定义复制构造函数和复制分配运算符,以使角色阵列的深层副本:

// 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;
}

请注意初始化和分配之间的差异:
在将旧状态分配给名称以防止内存泄漏之前,我们必须拆除旧状态。
另外,我们必须防止表单x = x的自我分配。
没有该检查,delete []名称将删除包含 source 字符串的数组,
因为当您编写x = x时,this-> namethat.name都包含相同的指针。

例外安全性

不幸的是,如果新的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;
}

这也可以在没有明确检查的情况下照顾自我分配。
解决此问题的更强大的解决方案是 copy-and-swap idiom
但是我不会在这里介绍例外安全的细节。
我只提到了一个例外来提出以下几点:编写管理资源的课程很难。

不可复制的资源

某些资源不能或不应该复制,例如文件句柄或静音。
在这种情况下,只需将复制构造函数和复制分配运算符声明为private而无需给出定义:

private:

    person(const person& that);
    person& operator=(const person& that);

另外,您可以从boost :: nontocobleable中继承或将其声明为已删除(在C ++ 11及以上):

person(const person& that) = delete;
person& operator=(const person& that) = delete;

三个规则

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

如果您需要明确声明灾难,
复制构造函数或复制作业操作员,
您可能需要明确声明所有三个。

(不幸的是,此“规则”不是由C ++标准或我知道的任何编译器强制执行的。)

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
};

零规则

3/5的规则也称为0/3/5的规则。该规则的零部分指出,创建类时,您不得写任何特殊成员功能。

大多数时候建议

您自己管理资源,
因为现有类(例如std :: string已经为您完成)。
只需使用std :: String成员比较简单的代码
使用char*进行复杂且容易出错的替代方案,您应该说服您。
只要您远离原始指针成员,三个规则就不太可能与您自己的代码有关。

Introduction

C++ treats variables of user-defined types with value semantics.
This means that objects are implicitly copied in various contexts,
and we should understand what "copying an object" actually means.

Let us consider a simple example:

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?
}

(If you are puzzled by the name(name), age(age) part,
this is called a member initializer list.)

Special member functions

What does it mean to copy a person object?
The main function shows two distinct copying scenarios.
The initialization person b(a); is performed by the copy constructor.
Its job is to construct a fresh object based on the state of an existing object.
The assignment b = a is performed by the copy assignment operator.
Its job is generally a little more complicated
because the target object is already in some valid state that needs to be dealt with.

Since we declared neither the copy constructor nor the assignment operator (nor the destructor) ourselves,
these are implicitly defined for us. Quote from the standard:

The [...] copy constructor and copy assignment operator, [...] and destructor are special member functions.
[ Note: The implementation will implicitly declare these member functions
for some class types when the program does not explicitly declare them.

The implementation will implicitly define them if they are used. [...] end note ]
[n3126.pdf section 12 §1]

By default, copying an object means copying its members:

The implicitly-defined copy constructor for a non-union class X performs a memberwise copy of its subobjects.
[n3126.pdf section 12.8 §16]

The implicitly-defined copy assignment operator for a non-union class X performs memberwise copy assignment
of its subobjects.
[n3126.pdf section 12.8 §30]

Implicit definitions

The implicitly-defined special member functions for person look like this:

// 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()
{
}

Memberwise copying is exactly what we want in this case:
name and age are copied, so we get a self-contained, independent person object.
The implicitly-defined destructor is always empty.
This is also fine in this case since we did not acquire any resources in the constructor.
The members' destructors are implicitly called after the person destructor is finished:

After executing the body of the destructor and destroying any automatic objects allocated within the body,
a destructor for class X calls the destructors for X's direct [...] members
[n3126.pdf 12.4 §6]

Managing resources

So when should we declare those special member functions explicitly?
When our class manages a resource, that is,
when an object of the class is responsible for that resource.
That usually means the resource is acquired in the constructor
(or passed into the constructor) and released in the destructor.

Let us go back in time to pre-standard C++.
There was no such thing as std::string, and programmers were in love with pointers.
The person class might have looked like this:

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;
    }
};

Even today, people still write classes in this style and get into trouble:
"I pushed a person into a vector and now I get crazy memory errors!"
Remember that by default, copying an object means copying its members,
but copying the name member merely copies a pointer, not the character array it points to!
This has several unpleasant effects:

  1. Changes via a can be observed via b.
  2. Once b is destroyed, a.name is a dangling pointer.
  3. If a is destroyed, deleting the dangling pointer yields undefined behavior.
  4. Since the assignment does not take into account what name pointed to before the assignment,
    sooner or later you will get memory leaks all over the place.

Explicit definitions

Since memberwise copying does not have the desired effect, we must define the copy constructor and the copy assignment operator explicitly to make deep copies of the character array:

// 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;
}

Note the difference between initialization and assignment:
we must tear down the old state before assigning it to name to prevent memory leaks.
Also, we have to protect against the self-assignment of the form x = x.
Without that check, delete[] name would delete the array containing the source string,
because when you write x = x, both this->name and that.name contain the same pointer.

Exception safety

Unfortunately, this solution will fail if new char[...] throws an exception due to memory exhaustion.
One possible solution is to introduce a local variable and reorder the statements:

// 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;
}

This also takes care of self-assignment without an explicit check.
An even more robust solution to this problem is the copy-and-swap idiom,
but I will not go into the details of exception safety here.
I only mentioned exceptions to make the following point: Writing classes that manage resources is hard.

Noncopyable resources

Some resources cannot or should not be copied, such as file handles or mutexes.
In that case, simply declare the copy constructor and copy assignment operator as private without giving a definition:

private:

    person(const person& that);
    person& operator=(const person& that);

Alternatively, you can inherit from boost::noncopyable or declare them as deleted (in C++11 and above):

person(const person& that) = delete;
person& operator=(const person& that) = delete;

The rule of three

Sometimes you need to implement a class that manages a resource.
(Never manage multiple resources in a single class,
this will only lead to pain.)
In that case, remember the rule of three:

If you need to explicitly declare either the destructor,
copy constructor or copy assignment operator yourself,
you probably need to explicitly declare all three of them.

(Unfortunately, this "rule" is not enforced by the C++ standard or any compiler I am aware of.)

The rule of five

From C++11 on, an object has 2 extra special member functions: the move constructor and move assignment. The rule of five states to implement these functions as well.

An example with the signatures:

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
};

The rule of zero

The rule of 3/5 is also referred to as the rule of 0/3/5. The zero part of the rule states that you are allowed to not write any of the special member functions when creating your class.

Advice

Most of the time, you do not need to manage a resource yourself,
because an existing class such as std::string already does it for you.
Just compare the simple code using a std::string member
to the convoluted and error-prone alternative using a char* and you should be convinced.
As long as you stay away from raw pointer members, the rule of three is unlikely to concern your own code.

无需解释 2025-02-17 07:29:21

三个规则说

如果您的班级需要任何

  • a 复制构造函数
  • 分配操作员
  • destructor

定义的解释,然后可能需要所有三个

这样做的原因是,这三个通常都用于管理资源,如果您的班级管理资源,通常需要管理复制和释放。

如果没有良好的语义来复制您的班级管理的资源,请考虑通过声明(不是

(请注意,即将推出的C ++标准的新版本(即C ++ 11)将移动语义添加到C ++,这可能会改变三个规则。但是,我对此一无所知,无法编写C ++ 11节。关于三个规则。)

The Rule of Three is a rule of thumb for C++, basically saying

If your class needs any of

  • a copy constructor,
  • an assignment operator,
  • or a destructor,

defined explictly, then it is likely to need all three of them.

The reasons for this is that all three of them are usually used to manage a resource, and if your class manages a resource, it usually needs to manage copying as well as freeing.

If there is no good semantic for copying the resource your class manages, then consider to forbid copying by declaring (not defining) the copy constructor and assignment operator as private.

(Note that the forthcoming new version of the C++ standard (which is C++11) adds move semantics to C++, which will likely change the Rule of Three. However, I know too little about this to write a C++11 section about the Rule of Three.)

嘿嘿嘿 2025-02-17 07:29:21

三巨头的定律如上所述。

简单的英语解决了一个简单的例子,即它解决的问题:

非默认的destructor

您在构造函数中分配了内存,因此您需要编写一个destructor来删除它。否则,您将导致内存泄漏。

您可能会认为这是工作。

问题将是,如果是对您的对象制作的副本,则该副本将指向与原始对象相同的内存。

曾经,其中一个删除了其破坏函数中的内存,另一个将在尝试使用它时具有无效的内存指针(这称为悬挂指针)。

因此,您编写一个复制构造函数,以便将新对象分配给他们自己的内存片段销毁。

分配运算符和复制构造函数

您将构造函数中的内存分配给了类的会员指针。当您复制此类的对象时,默认分配运算符和复制构造函数将将此成员指针的值复制到新对象。

这意味着新对象和旧对象将指向同一内存,因此当您在一个对象中更改它时,它也会更改另一个对象。如果一个对象删除此内存,则另一个对象将继续尝试使用它-EEK。

为了解决此问题,您可以编写自己的版本的复制构造函数和分配运算符。您的版本将内存分配给新对象,并跨第一个指针指向的值而不是其地址复制。

The law of the big three is as specified above.

An easy example, in plain English, of the kind of problem it solves:

Non default destructor

You allocated memory in your constructor and so you need to write a destructor to delete it. Otherwise you will cause a memory leak.

You might think that this is job done.

The problem will be, if a copy is made of your object, then the copy will point to the same memory as the original object.

Once, one of these deletes the memory in its destructor, the other will have a pointer to invalid memory (this is called a dangling pointer) when it tries to use it things are going to get hairy.

Therefore, you write a copy constructor so that it allocates new objects their own pieces of memory to destroy.

Assignment operator and copy constructor

You allocated memory in your constructor to a member pointer of your class. When you copy an object of this class the default assignment operator and copy constructor will copy the value of this member pointer to the new object.

This means that the new object and the old object will be pointing at the same piece of memory so when you change it in one object it will be changed for the other objerct too. If one object deletes this memory the other will carry on trying to use it - eek.

To resolve this you write your own version of the copy constructor and assignment operator. Your versions allocate separate memory to the new objects and copy across the values that the first pointer is pointing to rather than its address.

染柒℉ 2025-02-17 07:29:21

基本上,如果您有攻击函数(不是默认驱动器),则意味着您定义的类具有内存分配。假设该类是由某些客户端代码或您在外面使用的。

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

如果MyClass只有一些原始打字成员,则默认分配运算符将有效,但是如果它具有一些指针成员和对象,而这些成员和对象没有分配运算符,则结果将是无法预测的。因此,我们可以说,如果在班级的破坏者中要删除某些内容,我们可能需要一个深层的操作员,这意味着我们应该提供复制的构造函数和分配运算符。

Basically if you have a destructor (not the default destructor) it means that the class that you defined has some memory allocation. Suppose that the class is used outside by some client code or by you.

    MyClass x(a, b);
    MyClass y(c, d);
    x = y; // This is a shallow copy if assignment operator is not provided

If MyClass has only some primitive typed members a default assignment operator would work but if it has some pointer members and objects that do not have assignment operators the result would be unpredictable. Therefore we can say that if there is something to delete in destructor of a class, we might need a deep copy operator which means we should provide a copy constructor and assignment operator.

独木成林 2025-02-17 07:29:21

复制对象的含义是什么?
您可以通过几种方法复制对象 - Loet谈论您最有可能引用的两种,即深副本和浅副本。

由于我们采用一种面向对象的语言(或至少在假设是这样),因此假设您的记忆分配了。由于它是一种OO语言,因此我们可以轻松地指我们分配的大量内存,因为它们通常是原始变量(INT,chars,bytes)或我们定义的类型和原始类型的类别。因此,假设我们有一类汽车,如下所示:

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;
}

}

深层副本是如果我们声明对象,然后创建对象的完全独立的副本……我们最终在2个完全内存中添加了2个对象。

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.

现在让我们做一些奇怪的事情。假设CAR2是错误的,或者是故意分享CAR1所制成的实际记忆。 (这样做通常是一个错误是。

//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.*/

因此,无论您用哪种语言写的语言,都要非常谨慎地复制对象的含义,因为大多数时候您想要深层副本。

什么是复制构造函数和复制分配运算符?
我已经在上面使用了它们。当您键入诸如car2 = car1;之类的代码时,将调用复制构造函数,如果您声明一个变量并将其分配为一行,则是调用复制构造函数的时候。分配运算符是当您使用平等符号时会发生什么 - car2 = car1;。注意car2在同一语句中未声明。您为这些操作编写的两个代码可能非常相似。实际上,典型的设计模式具有您调用的另一个函数来设置所有满足后的所有内容,即初始副本/分配是合法的 - 如果您查看我编写的Longhand代码,则这些功能几乎相同。

我什么时候需要自己声明它们?
如果您不编写要以某种方式共享或生产的代码,那么您实际上只需要在需要时声明它们。您确实需要意识到如果您选择“偶然”使用它,而没有制作程序语言,则需要默认使用编译器。例如,我很少使用复制构造函数,但是分配运算符覆盖很常见。您是否知道您可以覆盖什么添加,减法等也意味着什么?

如何防止我的物体被复制?
覆盖使用私有功能为对象分配内存的所有方法都是合理的开始。如果您真的不希望人们复制它们,则可以将其公开,并通过抛出异常并不复制对象来提醒程序员。

What does copying an object mean?
There are a few ways you can copy objects--let's talk about the 2 kinds you're most likely referring to--deep copy and shallow copy.

Since we're in an object-oriented language (or at least are assuming so), let's say you have a piece of memory allocated. Since it's an OO-language, we can easily refer to chunks of memory we allocate because they are usually primitive variables (ints, chars, bytes) or classes we defined that are made of our own types and primitives. So let's say we have a class of Car as follows:

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;
}

}

A deep copy is if we declare an object and then create a completely separate copy of the object...we end up with 2 objects in 2 completely sets of memory.

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.

Now let's do something strange. Let's say car2 is either programmed wrong or purposely meant to share the actual memory that car1 is made of. (It's usually a mistake to do this and in classes is usually the blanket it's discussed under.) Pretend that anytime you ask about car2, you're really resolving a pointer to car1's memory space...that's more or less what a shallow copy is.

//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.*/

So regardless of what language you're writing in, be very careful about what you mean when it comes to copying objects because most of the time you want a deep copy.

What are the copy constructor and the copy assignment operator?
I have already used them above. The copy constructor is called when you type code such as Car car2 = car1; Essentially if you declare a variable and assign it in one line, that's when the copy constructor is called. The assignment operator is what happens when you use an equal sign--car2 = car1;. Notice car2 isn't declared in the same statement. The two chunks of code you write for these operations are likely very similar. In fact the typical design pattern has another function you call to set everything once you're satisfied the initial copy/assignment is legitimate--if you look at the longhand code I wrote, the functions are nearly identical.

When do I need to declare them myself?
If you are not writing code that is to be shared or for production in some manner, you really only need to declare them when you need them. You do need to be aware of what your program language does if you choose to use it 'by accident' and didn't make one--i.e. you get the compiler default. I rarely use copy constructors for instance, but assignment operator overrides are very common. Did you know you can override what addition, subtraction, etc. mean as well?

How can I prevent my objects from being copied?
Override all of the ways you're allowed to allocate memory for your object with a private function is a reasonable start. If you really don't want people copying them, you could make it public and alert the programmer by throwing an exception and also not copying the object.

那些过往 2025-02-17 07:29:21

我什么时候需要自己声明它们?

三个规则指出,如果您声明任何

  1. 复制构造函数
  2. 副本分配运算符
  3. 破坏者

,则应声明这三个。它从观察到的观察结果中得出,即需要接管副本操作的含义几乎总是源于执行某种资源管理的班级,并且几乎总是暗示

  • 在一个副本操作中正在进行任何资源管理可能需要在其他副本操作中完成,

  • 类destuructor也将参与资源(通常释放它)。要管理的经典资源是内存,这就是为什么所有标准库类都
    管理内存(例如,执行动态内存管理的STL容器)都声明“三巨头”:复制操作和击曲线。

三个规则的结果是,用户宣布的驱动器的存在表明,简单的成员明智副本不太可能适合同类中的复制操作。反过来,这表明,如果一类声明灾难,则可能不应自动生成复制操作,因为它们不会做正确的事情。在采用C ++ 98时,这种推理线的重要性尚未得到完全理解,因此在C ++ 98中,用户声明的destructor的存在对编译器的生成复制操作的意愿没有影响。在C ++ 11中仍然是这种情况,但这仅仅是因为限制了生成复制操作的条件会破坏太多的遗留代码。

如何防止复制我的对象?

声明复制构造函数&将分配运营商复制为私人访问说明符。

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);
}

在C ++ 11中,您还可以声明复制构造函数&amp;分配运营商已删除

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}

When do I need to declare them myself?

The Rule of Three states that if you declare any of a

  1. copy constructor
  2. copy assignment operator
  3. destructor

then you should declare all three. It grew out of the observation that the need to take over the meaning of a copy operation almost always stemmed from the class performing some kind of resource management, and that almost always implied that

  • whatever resource management was being done in one copy operation probably needed to be done in the other copy operation and

  • the class destructor would also be participating in management of the resource (usually releasing it). The classic resource to be managed was memory, and this is why all Standard Library classes that
    manage memory (e.g., the STL containers that perform dynamic memory management) all declare “the big three”: both copy operations and a destructor.

A consequence of the Rule of Three is that the presence of a user-declared destructor indicates that simple member wise copy is unlikely to be appropriate for the copying operations in the class. That, in turn, suggests that if a class declares a destructor, the copy operations probably shouldn’t be automatically generated, because they wouldn’t do the right thing. At the time C++98 was adopted, the significance of this line of reasoning was not fully appreciated, so in C++98, the existence of a user declared destructor had no impact on compilers’ willingness to generate copy operations. That continues to be the case in C++11, but only because restricting the conditions under which the copy operations are generated would break too much legacy code.

How can I prevent my objects from being copied?

Declare copy constructor & copy assignment operator as private access specifier.

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);
}

In C++11 onwards you can also declare copy constructor & assignment operator deleted

class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete

// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};


int main()
{
   MemoryBlock a;
   MemoryBlock b(a);
}
病女 2025-02-17 07:29:21

现有的许多答案已经触及了复制构造函数,分配操作员和破坏者。
但是,在C ++帖子11中,移动语义的引入可能会扩展到3。

最近迈克尔·克莱斯(Michael Claisse)发表了一个涉及此主题的演讲:
http://channel9.msdn.com/events /cpp/c-pp-con-2014/ - 典型级

Many of the existing answers already touch the copy constructor, assignment operator and destructor.
However, in post C++11, the introduction of move semantic may expand this beyond 3.

Recently Michael Claisse gave a talk that touches this topic:
http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class

勿忘初心 2025-02-17 07:29:21

C ++中的三个规则是设计的基本原则,也是三个要求的开发,如果以下成员函数之一有明确的定义,那么程序员应将其他两个成员一起定义。即,以下三个成员功能是必不可少的:驱动器,复制构造函数,复制分配运算符。

C ++中的复制构造函数是一个特殊的构造函数。它用于构建一个新对象,该对象是等于现有对象副本的新对象。

复制分配运算符是一个特殊的分配运算符,通常用于向相同类型对象的其他对象指定现有对象。

有快速的例子:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;

Rule of three in C++ is a fundamental principle of the design and the development of three requirements that if there is clear definition in one of the following member function, then the programmer should define the other two members functions together. Namely the following three member functions are indispensable: destructor, copy constructor, copy assignment operator.

Copy constructor in C++ is a special constructor. It is used to build a new object, which is the new object equivalent to a copy of an existing object.

Copy assignment operator is a special assignment operator that is usually used to specify an existing object to others of the same type of object.

There are quick examples:

// default constructor
My_Class a;

// copy constructor
My_Class b(a);

// copy constructor
My_Class c = a;

// copy assignment operator
b = a;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文