ПроблемаОперация копирования, изменяющая исходный операнд, происходит, когда конструктор копии или оператор назначения копии изменяет изменяемый представитель данных его исходного операнда.
Для образца, этот конструктор копирования 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 создается. The 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);
}