0

Оператор присваивания по умолчанию в C++ выполняет поверхностное копирование?

11

У меня есть простой вопрос, на который я не смог найти точного ответа. Является ли оператор присваивания по умолчанию (`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;

Похоже, это правда, когда я провожу тесты, но мне нужно убедиться, что я не упускаю какого-то специфического случая.

5 ответ(ов)

0

По моему мнению, стандартный operator= выполняет копирование. Он копирует каждое поле объекта.

Различия между поверхностным (shallow) и глубоким (deep) копированием возникает только в том случае, если копируемые поля являются указателями или имеют какое-то отложенное представление. Что касается стандартного operator=, то именно от полей зависит, что означает "копирование" — это может быть как глубокое, так и поверхностное.

В частности, при копировании обычного указателя просто копируется значение указателя, сам объект, на который он ссылается, не затрагивается. Поэтому объекты, содержащие указатели, по умолчанию копируются поверхностно при использовании стандартного operator=.

Существует множество попыток создать «умные» указатели, которые выполняют клонирование при копировании. Если вы используете их вместо обычных указателей, то стандартный operator= будет выполнять глубокое копирование.

Если ваш объект содержит стандартные контейнеры в качестве полей, то может быть запутанным (например, для программиста на Java) утверждать, что operator= выполняет "поверхностное копирование". В Java член Vector на самом деле является лишь ссылкой, поэтому "поверхностное копирование" означает, что члены Vector не клонируются: исходный и целевой объекты ссылаются на один и тот же внутренний объект вектора. В C++ член vector будет скопирован вместе с его содержимым, поскольку он является реальным объектом, а не ссылкой (и vector::operator= гарантирует, что содержимое также будет скопировано).

Если ваш член данных представляет собой вектор указателей, тогда у вас нет ни глубокого, ни поверхностного копирования. У вас получается полуглубокое копирование: исходные и целевые объекты имеют отдельные векторы, но соответствующие элементы векторов по-прежнему указывают на один и тот же, не клонированный объект.

0

В C++ понятия "поверхностное" (shallow) и "глубокое" (deep) копирование имеют меньшую значимость, чем в C или Java. Давайте разберем это на примере класса Foo, который состоит из трех компонентов: int, int* и std::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* и std::vector<int>: указатель int* одинаковый в f1 и f2, что можно считать "поверхностным копированием". В то же время std::vector<int> отличается между f1 и f2, что соответствует "глубокому копированию".

Что на самом деле произошло, так это то, что в C++ стандартный operator= ведет себя так, как будто для всех его членов вызываются соответствующие operator= по порядку. operator= для int, int* и других примитивных типов просто выполняет побайтовое поверхностное копирование. А вот operator= для std::vector<T> выполняет глубокое копирование.

Таким образом, можно сказать, что ответ на вопрос: нет, стандартный оператор присваивания в C++ не выполняет поверхностное копирование. Но и глубокое копирование он тоже не выполняет. Стандартный оператор присваивания в C++ рекурсивно применяет операторы присваивания к членам класса.

0

Да, он просто копирует объект по членам, что может вызвать проблемы с сырыми указателями.

0

Нет. Оператор operator= не выполняет копирование. Это оператор присваивания, а не копирования.

Оператор присваивания по умолчанию присваивает значения каждому члену класса.

0

Если a, b и c являются классами, то при присвоении будет вызван оператор присваивания этих классов, а не просто произойдет копирование сырых данных в памяти. Однако, как уже упоминали другие, сырые указатели будут скопированы без какой-либо попытки дублирования того, на что они указывают, что может привести к возникновению висячих указателей.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь