Оператор присваивания по умолчанию = в С++ - это мелкая копия?
Просто простой вопрос, который я не мог найти в любом другом месте. Является ли оператор по умолчанию = просто мелкой копией всех членов класса с правой стороны?
Class foo {
public:
int a, b, c;
};
foo f1, f2;
...
f1 = f2;
будет идентичным:
f1.a = f2.a;
f1.b = f2.b;
f1.c = f2.c;
Это кажется правдой, когда я тестирую его, но я должен быть уверен, что я не пропущу какой-то конкретный случай.
Ответы
Ответ 1
Я бы сказал, что по умолчанию operator=
- это копия. Он копирует каждого участника.
Различие между мелкой копией и глубокой копией не возникает, если только скопированные члены не являются какой-то косвенностью, такой как указатель. Что касается значения по умолчанию operator=
, то до того, как член будет скопирован, что означает "копия", он может быть глубоким или мелким.
В частности, хотя копирование необработанного указателя просто копирует значение указателя, оно не делает ничего с referand. Таким образом, объекты, содержащие элементы указателя, по умолчанию помечены operator=
.
Существуют различные усилия при написании интеллектуальных указателей, которые выполняют операции клонирования при копировании, поэтому, если вы используете их везде вместо необработанных указателей, то по умолчанию operator=
будет выполняться глубокая копия.
Если у вашего объекта есть стандартные контейнеры в качестве членов, то может (например) программист Java сказать, что operator=
является "мелкой копией". В Java член Vector
действительно является ссылкой, поэтому "мелкая копия" означает, что члены Vector
не клонированы: источник и назначение относятся к одному и тому же базовому векторному объекту. В С++ элемент Vector
будет скопирован вместе с его содержимым, поскольку элемент является фактическим объектом, а не ссылкой (и vector::operator=
гарантирует, что содержимое скопировано с ним).
Если ваш элемент данных является вектором указателей, то у вас нет ни глубокой копии, ни мелкой копии. У вас есть полу-глубокая копия, где исходный и целевой объекты имеют отдельные векторы, но соответствующие векторные элементы из каждого объекта все еще указывают на один и тот же незакрашенный объект.
Ответ 2
Да, значение по умолчанию operator=
является мелкой копией.
Кстати, различие фактическое между shallow copy
и deep copy
становится видимым, когда класс имеет указатели как поля-члены. В отсутствие указателей нет никакой разницы (насколько мне известно)!
Чтобы узнать разницу между ними, просмотрите эти темы (в самом стеке):
Ответ 3
Да, он просто копирует объект-мудрый, что может вызвать проблемы для raw-указателей.
Ответ 4
"мелкая" и "глубокая" копия менее значима в С++, чем в C или Java.
Чтобы проиллюстрировать это, я изменил класс Foo
с трех int
на int
, int*
и 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*
и vector<int>
: int*
одинаково в f1 и f2; это то, что вы назвали бы "мелкой копией". Однако vector<int>
отличается между f1 и f2; это то, что вы назвали бы "глубокой копией".
Что на самом деле произошло здесь, так это то, что по умолчанию operator =
в С++ ведет себя так, как если бы operator =
для всех его членов вызывался по порядку. operator =
для int
s, int*
s и другие примитивные типы - это просто мелкая мелкая копия. operator =
для vector<T>
выполняет глубокую копию.
Итак, я бы сказал, что ответ на вопрос: Нет, оператор присваивания по умолчанию в С++ не выполняет глубокую копию. Но он также не выполняет глубокую копию. Оператор присваивания по умолчанию в С++ рекурсивно применяет операторы присваивания членам класса.
Ответ 5
Если a, b и c были классами, тогда был бы выдан оператор присваивания для этих классов, поэтому компилятор не просто копирует содержимое необработанной памяти, но, как указывали другие, любые необработанные указатели будут скопированы без каких-либо попыток чтобы дублировать заостренную вещь, тем самым давая вам потенциал для оборванных указателей.
Ответ 6
Нет. operator=
не выполняет копию вообще. Это оператор присваивания, а не оператор копирования.
Оператор присваивания по умолчанию присваивает каждому члену.
Ответ 7
Как показано в приведенном ниже фрагменте кода, оператор = (присвоение) для 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;
}