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 создается. 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