Copy operation modifying source operand

Операция копирования изменяет представитель данных исходного объекта

Описание

Этот дефект возникает, когда конструктор копирования или оператор назначения копирования изменяет изменяемый представитель данных своего исходного операнда.

Для образца, этот конструктор копирования 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);
}

Информация о результатах

Группа: Объектно-ориентированная
Язык: C++
По умолчанию: On для рукописного кода, off для сгенерированного кода
Синтаксис командной строки : COPY_MODIFYING_SOURCE
Влияние: Средний
Введенный в R2018b