C++ 三分法则是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4172722/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is The Rule of Three?
提问by fredoverflow
- What does copying an objectmean?
- What are the copy constructorand the copy assignment operator?
- When do I need to declare them myself?
- How can I prevent my objects from being copied?
- 什么是抄袭的对象是什么意思?
- 什么是复制构造函数和复制赋值运算符?
- 我什么时候需要自己申报?
- 如何防止我的对象被复制?
采纳答案by fredoverflow
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.
C++ 使用值语义处理用户定义类型的变量。这意味着对象在各种上下文中被隐式复制,我们应该理解“复制对象”的实际含义。
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.)
(如果你name(name), age(age)
对这部分感到困惑,这被称为成员初始化列表。)
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.
复制person
对象是什么意思?该main
函数显示了两种不同的复制场景。初始化person b(a);
由复制构造函数执行。它的工作是根据现有对象的状态构造一个新对象。赋值b = a
由复制赋值运算符执行。它的工作通常稍微复杂一些,因为目标对象已经处于某种需要处理的有效状态。
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]
[...] 复制构造函数和复制赋值运算符,[...] 和析构函数是特殊的成员函数。[注意:当程序没有显式声明它们时,实现会为某些类类型隐式声明这些成员函数。如果使用它们,实现将隐式定义它们。[...]尾注] [n3126.pdf 第 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]
非联合类 X 的隐式定义的复制构造函数执行其子对象的成员复制。[n3126.pdf 节 12.8 §16]
非联合类 X 的隐式定义的复制赋值运算符执行其子对象的成员复制赋值。[n3126.pdf 节 12.8 §30]
Implicit definitions
隐含定义
The implicitly-defined special member functions for person
look like this:
隐式定义的特殊成员函数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()
{
}
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:
在这种情况下name
,age
成员复制正是我们想要的:
并且被复制,所以我们得到了一个自包含的、独立的person
对象。隐式定义的析构函数始终为空。在这种情况下这也很好,因为我们没有在构造函数中获取任何资源。成员的析构函数在person
析构函数完成后被隐式调用:
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]
在执行析构函数的主体并销毁主体内分配的任何自动对象之后,类 X 的析构函数调用 X 的直接 [...] 成员的析构函数 [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 responsiblefor that resource. That usually means the resource is acquiredin the constructor (or passed into the constructor) and releasedin 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:
让我们回到标准之前的 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;
}
};
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, notthe character array it points to!
This has several unpleasant effects:
即使在今天,人们仍然以这种风格编写类并遇到麻烦:“我将一个人推入了一个向量,现在我得到了疯狂的内存错误!”请记住,默认情况下,复制对象意味着复制其成员,但name
仅复制成员复制一个指针,而不是它指向的字符数组!这有几个令人不快的影响:
- Changes via
a
can be observed viab
. - Once
b
is destroyed,a.name
is a dangling pointer. - If
a
is destroyed, deleting the dangling pointer yields undefined behavior. - 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.
- 通过
a
可以观察到通过 的变化b
。 - 一旦
b
被销毁,a.name
就是一个悬空指针。 - 如果
a
被销毁,删除悬空指针会产生未定义的行为。 - 由于赋值没有考虑
name
赋值之前所指向的内容,迟早你会到处都是内存泄漏。
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 to name
to prevent memory leaks.
Also, we have to protect against self-assignment of the form x = x
.
Without that check, delete[] name
would delete the array containing the sourcestring,
because when you write x = x
, both this->name
and that.name
contain the same pointer.
注意初始化和赋值的区别:我们必须在赋值之前拆除旧状态name
以防止内存泄漏。此外,我们必须防止表单的自赋值x = x
。如果没有该检查,delete[] name
将删除包含源字符串的数组,因为当您写入时x = x
,this->name
和that.name
都包含相同的指针。
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:
不幸的是,如果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;
}
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.
这也可以在没有明确检查的情况下处理自分配。这个问题的一个更强大的解决方案是copy-and-swap idiom,但我不会在这里讨论异常安全的细节。我只提到了例外是为了说明以下几点:编写管理资源的类是很困难的。
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
不给出定义:
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):
或者,您可以继承boost::noncopyable
或声明它们为已删除(在 C++11 及更高版本中):
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.)
(不幸的是,这个“规则”不是由 C++ 标准或我知道的任何编译器强制执行的。)
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.
从 C++11 开始,一个对象有 2 个额外的特殊成员函数:移动构造函数和移动赋值。五国之治也执行这些功能。
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; // Copy Ctor
person(person &&) noexcept = default; // Move Ctor
person& operator=(const person &) = default; // Copy Assignment
person& operator=(person &&) noexcept = default; // Move Assignment
~person() noexcept = default; // 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.
3/5 规则也称为 0/3/5 规则。规则的零部分声明在创建类时不允许编写任何特殊成员函数。
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.
大多数情况下,您不需要自己管理资源,因为现有的类std::string
已经为您完成了。只需将使用std::string
成员的简单代码与使用 a的复杂且容易出错的替代方案进行比较char*
,您就会被说服。只要您远离原始指针成员,三原则就不太可能涉及您自己的代码。
回答by sbi
The Rule of Threeis a rule of thumb for C++, basically saying
该三的规则是拇指C ++的规则,基本上说
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
.
如果复制您的类管理的资源没有好的语义,则考虑通过将复制构造函数和赋值运算符声明(不定义)为来禁止复制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.)
(请注意,即将推出的新版本的 C++ 标准(即 C++11)为 C++ 添加了移动语义,这可能会改变三规则。但是,我对此知之甚少,无法编写 C++11 部分关于三规则。)
回答by Stefan
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.
这意味着新对象和旧对象将指向同一块内存,因此当您在一个对象中更改它时,另一个对象也会更改它。如果一个对象删除了这个内存,另一个对象将继续尝试使用它 - 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.
要解决这个问题,您可以编写自己的复制构造函数和赋值运算符版本。您的版本为新对象分配单独的内存,并复制第一个指针指向的值而不是其地址。
回答by fatma.ekici
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.
如果 MyClass 只有一些原始类型的成员,默认赋值运算符会起作用,但如果它有一些指针成员和没有赋值运算符的对象,结果将是不可预测的。因此我们可以说,如果在类的析构函数中有要删除的内容,我们可能需要一个深拷贝运算符,这意味着我们应该提供一个拷贝构造函数和赋值运算符。
回答by user1701047
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:
由于我们使用的是面向对象的语言(或者至少假设是这样),假设您分配了一块内存。因为它是一种面向对象的语言,我们可以很容易地引用我们分配的内存块,因为它们通常是原始变量(整数、字符、字节)或我们定义的由我们自己的类型和原始类型组成的类。所以假设我们有一个 Car 类,如下所示:
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.
深拷贝是如果我们声明一个对象,然后创建一个完全独立的对象副本……我们最终在 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.
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.
现在让我们做一些奇怪的事情。假设 car2 要么编程错误,要么故意共享构成 car1 的实际内存。(这样做通常是错误的,在课堂上通常是讨论它的毯子。)假设每当您询问 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.*/
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.
什么是复制构造函数和复制赋值运算符?我已经在上面使用过它们。当您键入代码时调用复制构造函数,例如,Car car2 = car1;
如果您声明一个变量并将其分配在一行中,则调用复制构造函数。赋值运算符是使用等号时发生的情况-- car2 = car1;
。通知car2
未在同一语句中声明。您为这些操作编写的两段代码可能非常相似。事实上,典型的设计模式有另一个函数,一旦您对初始复制/赋值合法感到满意,您就可以调用它来设置所有内容——如果您查看我编写的普通代码,这些函数几乎相同。
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.
如何防止我的对象被复制?使用私有函数覆盖允许为对象分配内存的所有方式是一个合理的开始。如果您真的不希望人们复制它们,您可以将其公开并通过抛出异常而不复制对象来提醒程序员。
回答by Ajay yadav
When do I need to declare them myself?
我什么时候需要自己申报?
The Rule of Three states that if you declare any of a
三法则规定,如果你声明任何一个
- copy constructor
- copy assignment operator
- 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.
在一个复制操作中进行的任何资源管理可能需要在另一个复制操作中完成,并且
类析构函数也将参与资源的管理(通常是释放它)。要管理的经典资源是内存,这就是为什么所有管理内存的标准库类(例如,执行动态内存管理的 STL 容器)都声明“三大”:复制操作和析构函数。
A consequence of the Rule of Threeis 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.
三规则的结果是用户声明的析构函数的存在表明简单的成员明智的复制不太可能适合类中的复制操作。反过来,这表明如果一个类声明了一个析构函数,复制操作可能不应该自动生成,因为它们不会做正确的事情。在 C++98 被采用的时候,这条推理线的重要性还没有被完全理解,所以在 C++98 中,用户声明的析构函数的存在对编译器生成复制操作的意愿没有影响。在 C++11 中仍然如此,但这仅仅是因为限制生成复制操作的条件会破坏太多遗留代码。
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
在 C++11 以后,您还可以声明复制构造函数和赋值运算符已删除
class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete
// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};
int main()
{
MemoryBlock a;
MemoryBlock b(a);
}
回答by wei
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.
许多现有答案已经涉及复制构造函数、赋值运算符和析构函数。但是,在 C++11 之后,移动语义的引入可能会将其扩展到 3 之外。
Recently Michael Claisse gave a talk that touches this topic: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class
最近 Michael Claisse 做了一个触及这个话题的演讲:http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class
回答by Marcus Thornton
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.
C++中的三规则是设计和开发三个要求的基本原则,如果以下成员函数中的一个有明确的定义,那么程序员应该一起定义其他两个成员函数。即以下三个成员函数缺一不可:析构函数、拷贝构造函数、拷贝赋值运算符。
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.
C++中的复制构造函数是一种特殊的构造函数。它用于构建新对象,新对象相当于现有对象的副本。
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;