Описание
Операция копии, изменяющая исходный операнд, происходит, когда конструктор копии или оператор присваивания копии изменяют изменяемый элемент данных его исходного операнда.
Например, этот конструктор копии A
изменяет элемент данных m
из его исходного операнда other
:
class A {
mutable int m;
public:
...
A(const A &other) : m(other.m) {
other.m = 0; //Modification of source
}
}
Риск
Операция копии с конструктором копии (или оператор присваивания копии):
className new_object = old_object; //Calls copy constructor of className
копирует его исходный операнд
old_object
к его целевому операнду
new_object
. После операции вы ожидаете, что целевой операнд будет копией немодифицированного исходного операнда. Если исходный операнд изменяется во время копии, это предположение нарушено.
Фиксация
Не изменяйте исходный операнд в операции копии.
Если вы изменяете исходный операнд в конструкторе копии, чтобы реализовать операцию пересылки, используйте конструктора перемещения вместо этого. Переместитесь конструкторы заданы на C++ 11 стандартов и позже.
Пример - копирует конструктора, изменяющего источник
#include <algorithm>
#include <vector>
class A {
mutable int m;
public:
A() : m(0) {}
explicit A(int m) : m(m) {}
A(const A &other) : m(other.m) {
other.m = 0;
}
A& operator=(const A &other) {
if (&other != this) {
m = other.m;
other.m = 0;
}
return *this;
}
int get_m() const { return m; }
};
void f() {
std::vector<A> v{10};
A obj(12);
std::fill(v.begin(), v.end(), obj);
}
В этом примере, векторе десяти объектов типа A
создается. std::fill
функционируйте копирует объект типа A
, который имеет элемент данных со значением 12 к каждому из десяти объектов. После этой операции вы можете ожидать, что все десять объектов в векторе имеют элемент данных со значением 12.
Однако первая копия изменяет элемент данных источника к значению 0. Остающиеся девять копий копируют это значение. После std::fill
вызовите, первый объект в векторе имеет элемент данных со значением 12, и остающиеся объекты имеют элементы данных со значением 0.
Коррекция — конструктор перемещения использования для изменения источника
Не изменяйте элементы данных исходного операнда в конструкторе копии или копируйте оператор присваивания. Если вы хотите, чтобы ваш класс имел операцию пересылки, используйте конструктора перемещения вместо конструктора копии.
В этом откорректированном примере, конструкторе копии и операторе присваивания копии класса A
не изменяйте элемент данных m
. Отдельный конструктор перемещения изменяет исходный операнд.
#include <algorithm>
#include <vector>
class A {
int m;
public:
A() : m(0) {}
explicit A(int m) : m(m) {}
A(const A &other) : m(other.m) {}
A(A &&other) : m(other.m) { other.m = 0; }
A& operator=(const A &other) {
if (&other != this) {
m = other.m;
}
return *this;
}
//Move constructor
A& operator=(A &&other) {
m = other.m;
other.m = 0;
return *this;
}
int get_m() const { return m; }
};
void f() {
std::vector<A> v{10};
A obj(12);
std::fill(v.begin(), v.end(), obj);
}