メインコンテンツ

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

ソース オペランドを変更するコピー操作

コピー操作によるソース オブジェクトのデータ メンバーの変更

説明

この欠陥は、コピー コンストラクターまたはコピー代入演算子によって、そのソース オペランドの変更可能なデータ メンバーが変更された場合に発生します。

たとえば、次のコピー コンストラクター A はそのソース オペランド other のデータ メンバー m を変更します。

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 型の 10 個のオブジェクトのベクトルを作成しています。関数 std::fill は、値 12 のデータ メンバーをもつ A 型のオブジェクトを、10 個の各オブジェクトにコピーします。この操作後、ベクトル内の 10 個のオブジェクトすべてが値 12 のデータ メンバーをもつと想定するかもしれません。

しかし、最初のコピーによってソースのデータ メンバーの値が 0 に変更されます。残りの 9 回のコピーではこの値がコピーされます。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
影響度: Medium

バージョン履歴

R2018b で導入