メインコンテンツ

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

CERT C++: OOP58-CPP

Copy operations must not mutate the source object

説明

ルール定義

コピー操作によってソース オブジェクトが変化してはなりません。1

Polyspace 実装

ルール チェッカーは、"ソース オペランドを変更するコピー操作" をチェックします。

すべて展開する

問題

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

たとえば、次のコピー コンストラクター 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; //Noncompliant
  }
   
  A& operator=(const A &other) {
    if (&other != this) {
      m = other.m;
      other.m = 0; //Noncompliant //Noncompliant
    }
    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);
}

チェック情報

グループ: 09.オブジェクト指向プログラミング (OOP)

バージョン履歴

R2019a で導入


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.