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

发布于 2024-10-18 14:20:35 字数 276 浏览 5 评论 0原文

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

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

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

与以下内容相同:

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

当我测试它时,这似乎是正确的,但我需要确保我没有错过一些特定的情况。

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?

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.

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

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

发布评论

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

评论(8

怼怹恏 2024-10-25 14:20:35

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

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

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

人们在编写在复制时执行克隆操作的智能指针方面做出了各种努力,因此,如果您在任何地方都使用这些指针来代替原始指针,那么默认的operator=将执行深层复制。

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

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

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

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.

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=.

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.

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 Vector member is really just a reference, so "shallow copy" means that Vector members aren't cloned: source and destination refer to the same underlying vector object. In C++ a vector member will be 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).

If your data member is a vector of pointers, then you don't have either a deep copy or a 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.

你是暖光i 2024-10-25 14:20:35

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

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

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

Yes, default operator= is a shallow copy.

By the way, the actual difference between shallow copy and deep copy becomes visible when the class has pointers as member fields. In the absence of pointers, there is no difference (to the best of my knowledge)!

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

盛夏已如深秋| 2024-10-25 14:20:35

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

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

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

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

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

int 很无聊,所以我把它省略了。但请看一下 int*vector 之间的区别:f1 和 f2 中的 int* 是相同的;这就是所谓的“浅拷贝”。然而,f1 和 f2 之间的向量是不同的;这就是所谓的“深拷贝”。

这里实际发生的情况是,C++ 中默认的 operator = 的行为就像按顺序调用其所有成员的 operator = 一样。 intint* 和其他基元类型的 operator = 只是按字节浅拷贝。 vectoroperator = 执行深层复制。

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

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

To illustrate this, I've changed your Foo class from three ints to an int, an int*, and 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 int is 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".

What's actually happened here is 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.

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.

白龙吟 2024-10-25 14:20:35

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

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

北音执念 2024-10-25 14:20:35

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

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

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

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

打印:

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

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

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 type is assigned by calling that member's assignment operator. Members that are of built-in type are 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
烟雨凡馨 2024-10-25 14:20:35

不。operator= 根本不执行复制。它是一个赋值运算符,而不是复制运算符。

默认赋值运算符对每个成员进行赋值。

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

The default assignment operator assigns each member.

远昼 2024-10-25 14:20:35

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

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.

囍孤女 2024-10-25 14:20:35

如下面的代码片段所示,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;
}

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

#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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文