Подтвердить что ты не робот

Оператор присваивания по умолчанию = в С++ - это мелкая копия?

Просто простой вопрос, который я не мог найти в любом другом месте. Является ли оператор по умолчанию = просто мелкой копией всех членов класса с правой стороны?

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

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

будет идентичным:

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

Это кажется правдой, когда я тестирую его, но я должен быть уверен, что я не пропущу какой-то конкретный случай.

4b9b3361

Ответ 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;
}