C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++でオブジェクトのコピーを防止する方法

C++では、クラスを定義すると、コピーコンストラクタやコピー代入演算子が暗黙的に生成されるため、オブジェクトを自由にコピーできてしまいます。しかし、リソースを独占的に管理するクラスやシングルトンパターンの実装など、設計上オブジェクトのコピーを禁止したいケースは少なくありません。

本記事では、C++でオブジェクトのコピーを防止するための代表的な手法を、コード例とともに解説します。

方法1:コピーコンストラクタと代入演算子をprivateにする

最も古典的な方法は、コピーコンストラクタとコピー代入演算子をprivateメンバとして宣言することです。これにより、クラス外部からこれらの関数を呼び出せなくなり、コピー操作を行おうとした時点でコンパイルエラーが発生します。

コード例

#include <iostream>
using namespace std;

class MyClass {
    int x;
public:
    MyClass() {
        // 引数なしコンストラクタ
    }
    MyClass(int y) : x(y) {
    }
private:
    MyClass(const MyClass& obj) : x(obj.x) {
        // privateなコピーコンストラクタ
    }
    MyClass& operator=(const MyClass& tmp_obj) {
        // privateな代入演算子(オーバーロード)
        x = tmp_obj.x;
        return *this;
    }
};

int main() {
    MyClass ob(50);
    MyClass ob2(ob); // コピーコンストラクタの呼び出し
    ob2 = ob;        // コピー代入演算子の呼び出し
}

実行結果(コンパイルエラー)

[Error] 'MyClass::MyClass(const MyClass&)' is private
[Error] within this context
[Error] 'MyClass& MyClass::operator=(const MyClass&)' is private
[Error] within this context

このように、コピーを試みた箇所で「privateメンバにはアクセスできない」というコンパイルエラーが出力され、コピーが防止されます。

方法2:privateなコピーコンストラクタを持つダミークラスを継承する

もう一つの方法は、コピーコンストラクタと代入演算子をprivateにしたダミークラス(基底クラス)を作成し、それを継承する方法です。派生クラスのコピーコンストラクタや代入演算子は、処理の中で自動的に基底クラスの対応する関数を呼び出そうとしますが、それらがprivateであるため呼び出せず、結果として派生クラスのコピーも禁止されます。

コード例

#include <iostream>
using namespace std;

class DummyClass {
public:
    DummyClass() {
    }
private:
    DummyClass(const DummyClass& temp_obj) {
    }
    DummyClass& operator=(const DummyClass& temp_obj) {
    }
};

class MyClass : public DummyClass {
    int x;
public:
    MyClass() { }
    MyClass(int y) : x(y) {
    }
};

int main() {
    MyClass ob1(50);
    MyClass ob2(ob1); // コピーコンストラクタの呼び出し
    ob2 = ob1;        // コピー代入演算子の呼び出し
}

実行結果(コンパイルエラー)

[Error] 'DummyClass::DummyClass(const DummyClass&)' is private
[Error] within this context
In function 'int main()':
[Note] synthesized method 'MyClass::MyClass(const MyClass&)' first required here
In member function 'MyClass& MyClass::operator=(const MyClass&)':
[Error] 'DummyClass& DummyClass::operator=(const DummyClass&)' is private
[Error] within this context
In function 'int main()':
[Note] synthesized method 'MyClass& MyClass::operator=(const MyClass&)' first required here

補足:C++11以降では「= delete」が推奨

C++11以降では、上記のようなテクニックを使わなくても、= delete を使って関数を明示的に削除(deleted定義)できます。意図が明確で可読性も高いため、現代のC++ではこちらの方法が推奨されます。

class MyClass {
    int x;
public:
    MyClass() = default;
    explicit MyClass(int y) : x(y) {}
    MyClass(const MyClass&) = delete;            // コピーを禁止
    MyClass& operator=(const MyClass&) = delete; // 代入を禁止
};

まとめ

  • コピーコンストラクタとコピー代入演算子をprivateに宣言することで、クラス外部からのコピーを防止できる。
  • privateなコピーコンストラクタを持つダミークラスを継承させても、同様にコピーを禁止できる。
  • C++11以降では = delete を使うのが最もシンプルで推奨される方法。
  1. C++で文字列をコピーする方法:strcpy()を使う場合と使わない場合のプログラム解説

    文字列(string)とは、null文字(\0)で終端される1次元のchar型配列のことです。ある文字列の値を別の文字列にコピーすることができます。コピーの方法には、標準ライブラリ関数であるstrcpy()を使用する方法と、使用せずに自前で処理する方法の2通りがあります。strcpy()を使わずに文字列をコピーするプログラムまずは、標準ライブラリに頼らず、forループを使って1文字ずつコピーする方法を見てみましょう。#include <iostream> using namespace std; int main() {     char s

  2. C/C++からPythonオブジェクトを操作する方法(Cython埋め込みの実践ガイド)

    C/C++からPythonオブジェクトを使うには?CやC++のプログラムからPythonオブジェクトを直接生成・操作したい場合、Cythonを利用するのが最も手軽な方法の一つです。ここでは、シンプルなPythonクラスをCythonでラップし、C言語側に組み込んで(埋め込んで)呼び出す例を紹介します。C++の場合も手順はほぼ同じで、コンパイル時に--cplusオプションを付けるだけで対応できます。ステップ1:Pythonクラスと公開関数を定義する(pycls.pyx)まず、ラップ対象となるPythonクラスと、C側から呼び出せるようにするためのcdef public関数を定義したファイル「py