赋值运算符与复制构造函数 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 22:22:34  来源:igfitidea点击:

assignment operator vs. copy constructor C++

c++pointersmemory-managementcopy-constructor

提问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 intVal1does 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 displayIntegerfunction. 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 displayIntegerdamages your intVal1object and the second call to displayIntegerdamages your intVal2object. After that both of your objects are broken, which is why the third displayIntegercall displays garbage.

如果您删除自己的复制构造函数,编译器将隐式地为您生成一个。编译器生成的拷贝构造函数将执行对象的拷贝。这将违反“三原则”并破坏类的功能,这正是您在实验中观察到的。基本上,第一个调用displayInteger损坏您的intVal1对象,第二个调用displayInteger损坏您的intVal2对象。之后你的两个对象都被破坏了,这就是第三个displayInteger调用显示垃圾的原因。

If you change the declaration of displayIntegerto

如果您将声明更改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. deletefrom the destructor invalidates the valuepointer of intVal1object, making it dangling pointer, dereferencing of which causes undefined behavior.

您遇到的问题是由默认复制构造函数引起的,它复制指针但不将其与新分配的内存相关联(就像您的复制构造函数实现那样)。当您按值传递对象时,会创建一个副本,当执行超出范围时,该副本将被销毁。delete来自析构函数valueintVal1对象的指针无效,使其成为悬空指针,取消引用会导致未定义的行为

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 deleteon 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 intVal1into the intObjparameter 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 intthat intVal1is. When displayIntegerreturns, intObjis destroyed, which will cause the intto be destroyed, and the pointer in intVal1to 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.

该副本将指向同intintVal1是。当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.

在更一般的层面上,通过删除复制构造函数,您违反了三原则,这通常会导致此类问题。