只是一个简单的快速问题,我在其他任何地方都找不到可靠的答案。默认的 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;
当我测试它时这似乎是正确的,但我需要确保我没有错过一些特定的情况。
只是一个简单的快速问题,我在其他任何地方都找不到可靠的答案。默认的 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;
当我测试它时这似乎是正确的,但我需要确保我没有错过一些特定的情况。
我会说,默认operator=
是一个副本。它复制每个成员。
除非被复制的成员是某种间接(例如指针),否则不会出现浅拷贝和深拷贝之间的区别。就默认operator=
值而言,“复制”的含义取决于被复制的成员,它可能是深的或浅的。
具体来说,虽然,复制原始指针只是复制指针值,它对引用没有任何作用。因此默认情况下包含指针成员的对象是浅拷贝的operator=
。
编写在复制时执行克隆操作的智能指针有多种努力,因此如果您在任何地方使用它们来代替原始指针,那么默认值operator=
将执行深度复制。
如果您的对象有任何标准容器作为成员,那么(例如)Java 程序员可能会混淆说这operator=
是“浅拷贝”。在 Java 中,Vector
成员实际上只是一个引用,因此“浅拷贝”意味着Vector
不会克隆成员:源和目标引用相同的底层向量对象。在 C++中,将vector
复制成员及其内容,因为该成员是实际对象而不是引用(并保证内容与它一起复制)。vector::operator=
如果您的数据成员是指针向量,那么您既没有深拷贝也没有浅拷贝。您有一个半深度副本,其中源对象和目标对象具有单独的向量,但来自每个对象的相应向量元素仍然指向相同的未克隆对象。
是的,默认operator=
是浅拷贝。
顺便说一句,当类将指针作为成员字段时,和之间的实际差异变得可见。在没有指针的情况下,没有区别(据我所知)!shallow copy
deep copy
要了解它们之间的区别,请参阅这些主题(关于 stackoverflow 本身):
是的,它只是按成员方式复制对象,这可能会导致原始指针出现问题。
“浅”与“深”副本在 C++ 中的意义不如在 C 或 Java 中。
为了说明这一点,我将您的Foo
课程从三个int
s 更改为 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;
}
当这个程序运行时,它会产生以下输出:
f1.b: 0x100100080 &f1.c[0]: 0x100100090
f2.b: 0x100100080 &f2.c[0]: 0x1001000a0
int
很无聊,所以我把它排除在外。int*
但是看一下 the和 the之间的区别vector<int>
:int*
f1 和 f2 中的 f 是一样的;这就是你所说的“浅拷贝”。然而vector<int>
,f1 和 f2 之间是不同的;这就是你所说的“深拷贝”。
这里实际发生的是,C++ 中的默认值operator =
的行为就好像它operator =
的所有成员都按顺序调用了一样。operator =
for int
s、sint*
和其他原始类型只是按字节进行的浅拷贝。operator =
forvector<T>
执行深层复制。
所以我想说这个问题的答案是,不,C++ 中的默认赋值运算符不执行浅拷贝。但它也不执行深层复制。C++ 中的默认赋值运算符递归地应用类成员的赋值运算符。
我个人喜欢Accelerated 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
如果 a、b 和 c 是类,则将调用这些类的赋值运算符,因此编译器不仅仅是复制原始内存内容 - 但正如其他人指出的那样,任何原始指针都将被复制,而不会尝试复制指向的东西,从而给你悬空指针的潜力。
不 operator=
,根本不执行副本。这是一个赋值运算符,而不是复制运算符。
默认分配运算符分配每个成员。
如下面的代码片段所示,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;
}