赋值运算符与复制构造函数 C++
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18969083/
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
assignment operator vs. copy constructor C++
提问by Lukas Bystricky
I have the following code to test out my understanding of basic pointers in C++:
我有以下代码来测试我对 C++ 中基本指针的理解:
// Integer.cpp
#include "Integer.h"
Integer::Integer()
{
value = new int;
*value = 0;
}
Integer::Integer( int intVal )
{
value = new int;
*value = intVal;
}
Integer::~Integer()
{
delete value;
}
Integer::Integer(const Integer &rhInt)
{
value = new int;
*value = *rhInt.value;
}
int Integer::getInteger() const
{
return *value;
}
void Integer::setInteger( int newInteger )
{
*value = newInteger;
}
Integer& Integer::operator=( const Integer& rhInt )
{
*value = *rhInt.value;
return *this;
}
// IntegerTest.cpp
#include <iostream>
#include <cstdlib>
#include "Integer.h"
using namespace std;
void displayInteger( char* str, Integer intObj )
{
cout << str << " is " << intObj.getInteger() << endl;
}
int main( int argc, char* argv[] )
{
Integer intVal1;
Integer intVal2(10);
displayInteger( "intVal1", intVal1 );
displayInteger( "intVal2", intVal2 );
intVal1 = intVal2;
displayInteger( "intVal1", intVal1 );
return EXIT_SUCCESS;
}
This code works exactly as expected as is, it prints out:
这段代码完全按预期工作,它打印出:
intVal1 is 0
intVal2 is 10
intVal1 is 10
However if I remove the copy constructor it prints out something like:
但是,如果我删除复制构造函数,它会打印出如下内容:
intVal1 is 0
intVal2 is 10
intVal1 is 6705152
I don't understand why this is the case. My understanding is that the copy constructor is used when the assignment is to an object that doesn't exist. Here intVal1
does exist, so why isn't the assignment operator called?
我不明白为什么会这样。我的理解是当赋值给一个不存在的对象时使用复制构造函数。这里intVal1
确实存在,那么为什么不调用赋值运算符呢?
采纳答案by AnT
The copy constructor is not used during assignment. Copy constructor in your case is used when passing arguments to displayInteger
function. The second parameter is passed by value, meaning that it is initailized by copy contructor.
赋值期间不使用复制构造函数。在您的情况下,复制构造函数在将参数传递给displayInteger
函数时使用。第二个参数是按值传递的,这意味着它是由复制构造函数初始化的。
Your version of copy constructor performs deepcopying of data owned by the class (just like your assignment operator does). So, everything works correctly with your version of copy constructor.
您的复制构造函数版本执行类拥有的数据的深度复制(就像您的赋值运算符一样)。因此,一切都与您的复制构造函数版本一起正常工作。
If you remove your own copy constructor, the compiler will generate one for you implicitly. The compiler-generated copy constructor will perform shallowcopying of the object. This will violate the "Rule of Three"and destroy the functionality of your class, which is exactly what you observe in your experiment. Basically, the first call to displayInteger
damages your intVal1
object and the second call to displayInteger
damages your intVal2
object. After that both of your objects are broken, which is why the third displayInteger
call displays garbage.
如果您删除自己的复制构造函数,编译器将隐式地为您生成一个。编译器生成的拷贝构造函数将执行对象的浅拷贝。这将违反“三原则”并破坏类的功能,这正是您在实验中观察到的。基本上,第一个调用displayInteger
损坏您的intVal1
对象,第二个调用displayInteger
损坏您的intVal2
对象。之后你的两个对象都被破坏了,这就是第三个displayInteger
调用显示垃圾的原因。
If you change the declaration of displayInteger
to
如果您将声明更改displayInteger
为
void displayInteger( char* str, const Integer &intObj )
your code will "work" even without an explicit copy constructor. But it is not a good idea to ignore the "Rule of Three"in any case. A class implemented in this way either has to obey the "Rule of Three" or has to be made non-copyable.
即使没有显式复制构造函数,您的代码也能“工作”。但无论如何,忽略“三分法则”并不是一个好主意。以这种方式实现的类要么必须遵守“三原则”,要么必须不可复制。
回答by LihO
The problem you're experiencing is caused by the default copy constructor, which copies the pointer but doesn't associate it with newly allocated memory (like your implementation of copy constructor does). When you pass object by value, a copy is created and when the execution goes out of scope, this copy is destructed. delete
from the destructor invalidates the value
pointer of intVal1
object, making it dangling pointer, dereferencing of which causes undefined behavior.
您遇到的问题是由默认复制构造函数引起的,它复制指针但不将其与新分配的内存相关联(就像您的复制构造函数实现那样)。当您按值传递对象时,会创建一个副本,当执行超出范围时,该副本将被销毁。delete
来自析构函数value
的intVal1
对象的指针无效,使其成为悬空指针,取消引用会导致未定义的行为。
Debug outputs might be used to understand the behavior of your code:
调试输出可用于了解代码的行为:
class Integer {
public:
Integer() {
cout << "ctor" << endl;
value = new int;
*value = 0;
}
~Integer() {
cout << "destructor" << endl;
delete value;
}
Integer(int intVal) {
cout << "ctor(int)" << endl;
value = new int;
*value = intVal;
}
Integer(const Integer &rhInt) {
cout << "copy ctor" << endl;
value = new int;
*value = *rhInt.value;
}
Integer& operator=(const Integer& rhInt){
cout << "assignment" << endl;
*value = *rhInt.value;
return *this;
}
int *value;
};
void foo(Integer intObj) {
cout << intObj.value << " " << *(intObj.value) << endl;
}
Now output of this code:
现在输出此代码:
Integer intVal1;
Integer intVal2(10);
foo( intVal1 );
foo( intVal2 );
intVal1 = intVal2;
foo( intVal1 );
is:
是:
ctor
ctor(int)
copy ctor
0x9ed4028 0
destructor
copy ctor
0x9ed4038 10
destructor
assignment
copy ctor
0x9ed4048 10
destructor
destructor
destructor
ctor
ctor(int)
copy ctor
0x9ed4028 0
析构函数
copy ctor
0x9ed4038 10
析构函数
赋值
copy ctor
0x9ed4048 10
析
构
函数destructor析构函数
which shows that copy constructor is used when passing objects by value. However, important to notice here is the destructor called upon the return from your function. And if you remove your implementation of copy constructor, then the output is:
这表明在按值传递对象时使用了复制构造函数。但是,这里需要注意的是在函数返回时调用的析构函数。如果删除复制构造函数的实现,则输出为:
ctor
ctor(int)
0x81340080
destructor
0x8134018 10
destructor
assignment
0x8134008135479296
destructor
destructor
destructor
ctor
ctor(int)
0x81340080
析构函数
0x8134018 10
析构函数
赋值
0x8134008135479296
析
构
函数析构函数析构函数
showing that the first copy called delete
on the same pointer (pointing to 0x8134008
) as was used by third copy later, where the memory pointed by this dangling pointer has been used.
显示第一个副本调用delete
的指针(指向0x8134008
)与后来的第三个副本使用的指针相同,其中使用了这个悬空指针指向的内存。
回答by Vaughn Cato
Think about this call:
想想这个电话:
displayInteger( "intVal1", intVal1 );
You are creating a copy of intVal1
into the intObj
parameter of displayInteger
:
您正在将 的副本创建intVal1
到 的intObj
参数中displayInteger
:
void displayInteger( char* str, Integer intObj )
{
cout << str << " is " << intObj.getInteger() << endl;
}
That copy will be pointing to the same int
that intVal1
is. When displayInteger
returns, intObj
is destroyed, which will cause the int
to be destroyed, and the pointer in intVal1
to be pointing to an invalid object. At that point all bets are off (A.K.A. undefined behavior) if you try to access the value. A similar thing happens for intVal2
.
该副本将指向同int
那intVal1
是。当displayInteger
返回时,intObj
被销毁,这将导致int
被销毁,并且指针intVal1
指向一个无效的对象。此时,如果您尝试访问该值,则所有赌注都将关闭(也称为未定义行为)。类似的事情发生在intVal2
.
At a more general level, by removing the copy constructor, you are violating the Rule of Three, which typically leads to these kinds of problems.
在更一般的层面上,通过删除复制构造函数,您违反了三原则,这通常会导致此类问题。