Оператор присваивания по умолчанию в 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;
Похоже, это правда, когда я провожу тесты, но мне нужно убедиться, что я не упускаю какого-то специфического случая.
5 ответ(ов)
По моему мнению, стандартный operator=
выполняет копирование. Он копирует каждое поле объекта.
Различия между поверхностным (shallow) и глубоким (deep) копированием возникает только в том случае, если копируемые поля являются указателями или имеют какое-то отложенное представление. Что касается стандартного operator=
, то именно от полей зависит, что означает "копирование" — это может быть как глубокое, так и поверхностное.
В частности, при копировании обычного указателя просто копируется значение указателя, сам объект, на который он ссылается, не затрагивается. Поэтому объекты, содержащие указатели, по умолчанию копируются поверхностно при использовании стандартного operator=
.
Существует множество попыток создать «умные» указатели, которые выполняют клонирование при копировании. Если вы используете их вместо обычных указателей, то стандартный operator=
будет выполнять глубокое копирование.
Если ваш объект содержит стандартные контейнеры в качестве полей, то может быть запутанным (например, для программиста на Java) утверждать, что operator=
выполняет "поверхностное копирование". В Java член Vector
на самом деле является лишь ссылкой, поэтому "поверхностное копирование" означает, что члены Vector
не клонируются: исходный и целевой объекты ссылаются на один и тот же внутренний объект вектора. В C++ член vector
будет скопирован вместе с его содержимым, поскольку он является реальным объектом, а не ссылкой (и vector::operator=
гарантирует, что содержимое также будет скопировано).
Если ваш член данных представляет собой вектор указателей, тогда у вас нет ни глубокого, ни поверхностного копирования. У вас получается полуглубокое копирование: исходные и целевые объекты имеют отдельные векторы, но соответствующие элементы векторов по-прежнему указывают на один и тот же, не клонированный объект.
В 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++ рекурсивно применяет операторы присваивания к членам класса.
Да, он просто копирует объект по членам, что может вызвать проблемы с сырыми указателями.
Нет. Оператор operator=
не выполняет копирование. Это оператор присваивания, а не копирования.
Оператор присваивания по умолчанию присваивает значения каждому члену класса.
Если a, b и c являются классами, то при присвоении будет вызван оператор присваивания этих классов, а не просто произойдет копирование сырых данных в памяти. Однако, как уже упоминали другие, сырые указатели будут скопированы без какой-либо попытки дублирования того, на что они указывают, что может привести к возникновению висячих указателей.
Как удалить элемент из std::vector<> по индексу?
`unsigned int` против `size_t`: когда и что использовать?
Какова разница между "new", "malloc" и "calloc" в C++?
Что означает && в конце сигнатуры функции (после закрывающей скобки)?
Инициализация std::string из char* без копирования