c++中的默认赋值运算符=是浅拷贝?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5096464/
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-28 17:22:50  来源:igfitidea点击:

Default assignment operator= in c++ is a shallow copy?

c++operatorsshallow-copy

提问by Christopher Dorian

Just a simple quick question which I couldn't find a solid answer to anywhere else. Is the default operator= just a shallow copy of all the class' members on the right hand side?

只是一个简单的快速问题,我在其他任何地方都找不到可靠的答案。默认的 operator= 只是右侧所有类成员的浅拷贝吗?

Class foo {
public:
  int a, b, c;
};

foo f1, f2;
...
f1 = f2;

would be identical to:

将等同于:

f1.a = f2.a;
f1.b = f2.b;
f1.c = f2.c;

This seems to be true when I test it but I need to be sure I'm not missing some specific case.

这在我测试时似乎是正确的,但我需要确保我没有遗漏某些特定情况。

回答by Steve Jessop

I'd say, default operator=is a copy. It copies each member.

我会说,默认operator=是一个副本。它复制每个成员。

The distinction between a shallow copy and a deep copy doesn't arise unless the members being copied are some kind of indirection such as a pointer. As far as the default operator=is concerned, it's up to the member being copied what "copy" means, it could be deep or shallow.

除非被复制的成员是某种间接类型,例如指针,否则不会出现浅拷贝和深拷贝之间的区别。就默认operator=而言,由被复制的成员决定“复制”的含义,它可以是深的也可以是浅的。

Specifically, though, copying a raw pointer just copies the pointer value, it doesn't do anything with the referand. So objects containing pointer members are shallow-copied by default operator=.

但具体来说,复制原始指针只是复制指针值,它对引用没有任何作用。因此,默认情况下,包含指针成员的对象是浅复制的operator=

There are various efforts at writing smart pointers that perform clone operations on copying, so if you use those everywhere in place of raw pointers then the default operator=will perform a deep copy.

在编写在复制时执行克隆操作的智能指针方面有多种努力,因此如果您在任何地方使用它们代替原始指针,那么默认值operator=将执行深度复制。

If your object has any standard containers as members, then it may be confusing to (for example) a Java programmer to say that operator=is a "shallow copy". In Java a Vectormember is really just a reference, so "shallow copy" means that Vectormembers aren't cloned: source and destination refer to the same underlying vector object. In C++ a vectormember willbe copied, along with its contents, since the member is an actual object not a reference (and vector::operator=guarantees the contents are copied with it).

如果您的对象具有任何标准容器作为成员,那么(例如)Java 程序员说这operator=是“浅拷贝”可能会令人困惑。在 Java 中,Vector成员实际上只是一个引用,因此“浅拷贝”意味着Vector成员不会被克隆:源和目标引用相同的底层向量对象。在 C++ 中,复制vector成员及其内容,因为该成员是实际对象而不是引用(并保证内容与其一起复制)。vector::operator=

If your data member is a vector of pointers, then you don't have either a deep copy ora shallow copy. You have a semi-deep copy, where the source and destination objects have separate vectors, but the corresponding vector elements from each still point to the same, uncloned object.

如果您的数据成员是指针向量,那么您既没有深拷贝没有浅拷贝。您有一个半深副本,其中源对象和目标对象具有单独的向量,但每个对象的相应向量元素仍然指向相同的未克隆对象。

回答by Nawaz

Yes, default operator=is a shallow copy.

是的,默认operator=是浅拷贝。

By the way, the actualdifference between shallow copyand deep copybecomes visible when the class has pointersas member fields. In the absence of pointers, there is no difference (to the best of my knowledge)!

顺便说一句,当类具有指针作为成员字段时,和之间的实际差异变得可见。在没有指针的情况下,没有区别(据我所知)!shallow copydeep copy

To know the difference between them, see these topics (on stackoverflow itself):

要了解它们之间的区别,请参阅以下主题(关于 stackoverflow 本身):

回答by Mark B

Yes, it just copies the object member-wise, which can cause issues for raw pointers.

是的,它只是按成员方式复制对象,这可能会导致原始指针出现问题。

回答by Hyman Saalw?chter

"shallow" versus "deep" copy is less meaningful in C++ than it is in C or Java.

“浅”与“深”复制在 C++ 中的意义不如在 C 或 Java 中。

To illustrate this, I've changed your Fooclass from three ints to an int, an int*, and a vector<int>:

为了说明这一点,我将您的Foo类从三个ints更改为 an int、 anint*和 a vector<int>

#include <iostream>
#include <vector>

class Foo {
public:
  int a;
  int *b;
  std::vector<int> c;
};

using namespace std;

int main() {
  Foo f1, f2;
  f1.a = 42;
  f1.b = new int(42);
  f1.c.push_back(42);
  f2 = f1;

  cout << "f1.b: " << f1.b << " &f1.c[0]: " << &f1.c[0] << endl;
  cout << "f2.b: " << f2.b << " &f2.c[0]: " << &f2.c[0] << endl;
}

When this program is run, it yields the following output:

当这个程序运行时,它会产生以下输出:

f1.b: 0x100100080 &f1.c[0]: 0x100100090
f2.b: 0x100100080 &f2.c[0]: 0x1001000a0

The intis boring, so I've left it out. But look at the difference between the int*and the vector<int>: the int*is the same in f1 and f2; it's what you would call a "shallow copy". The vector<int>however is different between f1 and f2; it's what you would call a "deep copy".

int是枯燥的,所以我离开了出来。但是看一下 theint*和 the的区别vector<int>: theint*在 f1 和 f2 中是一样的;这就是你所说的“浅拷贝”。的vector<int>但是是f1和f2之间不同; 这就是你所说的“深拷贝”。

What's actually happened hereis that the default operator =in C++ behaves as if the operator =for all of its members were called in order. The operator =for ints, int*s, and other primitive types is just a byte-wise shallow copy. The operator =for vector<T>performs a deep copy.

这里实际发生的情况是,operator =C++中的默认行为就好像它operator =的所有成员都按顺序调用一样。在operator =intS,int*S,和其他原始类型只是逐字节浅拷贝。该operator =用于vector<T>执行深层复制。

So I would say the answer to the question is, No, the default assignment operator in C++ does not perform a shallow copy. But it also doesn't perform a deep copy. The default assignment operator in C++ recursively applies the assignment operators of the class's members.

所以我会说这个问题的答案是,不,C++ 中的默认赋值运算符不执行浅拷贝。但它也不执行深拷贝。C++ 中的默认赋值运算符递归地应用类成员的赋值运算符。

回答by fabian

I personally like the explanation in Accelerated c++. The text (page 201) says:

我个人喜欢Accelerated c++ 中的解释。正文(第 201 页)说:

If the class author does not specify the assignment operator, the compiler synthesizes a default version. The default version is defined to operate recursively - assigning each data element according to the appropriate rules for the type of that element. Each member of a class typeis assigned by calling that member's assignment operator. Members that are of built-in typeare assigned by assigning their values.

如果类作者没有指定赋值运算符,编译器会合成一个默认版本。默认版本定义为递归操作 - 根据该元素类型的适当规则分配每个数据元素。类类型的每个成员都通过调用该成员的赋值运算符来赋值。内置类型的成员通过分配它们的值来分配。

As the members in the question are integers, I understand that they are deep copied. The following adaptation of your example illustrates this:

由于问题中的成员是整数,我知道它们被深度复制。您的示例的以下改编说明了这一点:

#include<iostream>
class foo {
public:
  int a, b, c;
};

int main() {
    foo f1, f2;
    f1 = f2;
    std::cout << "f1.a and f2.a are: " << f1.a << " and " << f2.a << std::endl;
    f2.a = 0;
    std::cout << "now, f1.a and f2.a are: " << f1.a << " and " << f2.a << std::endl;
}

which prints:

打印:

f1.a and f2.a are: 21861 and 21861
now, f1.a and f2.a are: 21861 and 0

回答by Edward Strange

No. operator=doesn't perform a copy at all. It's an assignmentoperator, not copyoperator.

operator=完全不执行复制操作。它是赋值运算符,而不是复制运算符。

The default assignment operator assigns each member.

默认赋值运算符为每个成员赋值。

回答by TimA

If a, b and c were classes then the assignment operator for those classes would be called, so the compiler isn't simply copying the raw memory contents - but as others pointed out, any raw pointers will be copied without any attempt to duplicate the pointed-to thing, thus giving you the potential for dangling pointers.

如果 a、b 和 c 是类,则将调用这些类的赋值运算符,因此编译器不是简单地复制原始内存内容 - 但正如其他人指出的那样,将复制任何原始指针,而不会尝试复制指向的东西,从而为您提供悬空指针的潜力。

回答by kallumama24

As illustrated by the code snippet below, the = (assignment) operator for STL performs a deep copy.

如下面的代码片段所示,STL 的 =(赋值)运算符执行深度复制。

#include <iostream>
#include <stack>
#include <map>
#include <vector>

using namespace std;

int main(int argc, const char * argv[]) {
    /* performs deep copy */
    map <int, stack<int> > m;
    stack <int> s1;
    stack <int> s2;

    s1.push(10);
    cout<<&s1<<" "<<&(s1.top())<<" "<<s1.top()<<endl;   //0x7fff5fbfe478 0x100801200 10

    m.insert(make_pair(0, s1));
    cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 10

    m[0].top() = 1;
    cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 1

    s2 = m[0];
    cout<<&s2<<" "<<&(s2.top())<<" "<<s2.top()<<endl;   //0x7fff5fbfe448 0x100804200 1

    s2.top() = 5;
    cout<<&s2<<" "<<&(s2.top())<<" "<<s2.top()<<endl;   //0x7fff5fbfe448 0x100804200 5
    cout<<&m[0]<<" "<<&(m[0].top())<<" "<<m[0].top()<<endl; //0x100104248 0x100803200 1

    cout<<endl<<endl;

    map <int, stack<int*> > mp;
    stack <int*> s1p;
    stack <int*> s2p;

    s1p.push(new int);
    cout<<&s1p<<" "<<&(s1p.top())<<" "<<s1p.top()<<endl;    //0x7fff5fbfe360 0x100805200 0x100104290

    mp.insert(make_pair(0, s1p));
    cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl;  //0x1001042e8 0x100806200 0x100104290

    mp[0].top() = new int;
    cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl;  //0x1001042e8 0x100806200 0x100104320

    s2p = mp[0];
    cout<<&s2p<<" "<<&(s2p.top())<<" "<<s2p.top()<<endl;    //0x7fff5fbfe330 0x100807200 0x100104320

    s2p.top() = new int;
    cout<<&s2p<<" "<<&(s2p.top())<<" "<<s2p.top()<<endl;    //0x7fff5fbfe330 0x100807200 0x100104340
    cout<<&mp[0]<<" "<<&(mp[0].top())<<" "<<mp[0].top()<<endl;  //0x1001042e8 0x100806200 0x100104320

    cout<<endl<<endl;

    vector<int> v1,v2;
    vector<int*> v1p, v2p;

    v1.push_back(1);
    cout<<&v1<<" "<<&v1[0]<<" "<<v1[0]<<endl;   //0x7fff5fbfe290 0x100104350 1

    v2 = v1;
    cout<<&v2<<" "<<&v2[0]<<" "<<v2[0]<<endl;   //0x7fff5fbfe278 0x100104360 1

    v2[0] = 10;
    cout<<&v2<<" "<<&v2[0]<<" "<<v2[0]<<endl;   //0x7fff5fbfe278 0x100104360 10
    cout<<&v1<<" "<<&v1[0]<<" "<<v1[0]<<endl;   //0x7fff5fbfe290 0x100104350 1

    cout<<endl<<endl;

    v1p.push_back(new int);
    cout<<&v1p<<" "<<&v1p[0]<<" "<<v1p[0]<<endl;    //0x7fff5fbfe260 0x100104380 0x100104370

    v2p = v1p;
    cout<<&v2p<<" "<<&v2p[0]<<" "<<v2p[0]<<endl;    //0x7fff5fbfe248 0x100104390 0x100104370

    v2p[0] = new int;
    cout<<&v2p<<" "<<&v2p[0]<<" "<<v2p[0]<<endl;    //0x7fff5fbfe248 0x100104390 0x1001043a0
    cout<<&v1p<<" "<<&v1p[0]<<" "<<v1p[0]<<endl;    //0x7fff5fbfe260 0x100104380 0x100104370

    return 0;
}