exponenta event banner

Копировать операцию изменения исходного операнда

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

Описание

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

Например, этот конструктор копирования 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);
}

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

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