C++のコピーコンストラクタについてわかりやすく詳しく解説

C++において、コピーコンストラクタはクラスを使いこなす上で重要な概念の一つです。

この記事では、C++初心者でも理解しやすいように、コピーコンストラクタの基本的な使い方を実際のサンプルコードを交えて詳しく解説します。

目次から探す

C++のコピーコンストラクタとは

C++のコピーコンストラクタとは、オブジェクトを別のオブジェクトにコピーするための特別なコンストラクタです。

通常、オブジェクトをコピーする場合、単純に代入演算子を使用することができますが、いくつかの場合ではコピーコンストラクタが必要になります。

例えば、動的にメモリを割り当てるオブジェクトや、ポインタを持つオブジェクトなどは、単純な代入演算子だけでは正しくコピーできません。このような場合には、コピーコンストラクタを定義しておく必要があります。

以下では、具体的な例を交えてC++のコピーコンストラクタについて詳しく解説します。

コピーコンストラクタの使い方

C++において、コピーコンストラクタはオブジェクトのコピーを作成するために使用されます。

コピーコンストラクタは、同じクラスの別のオブジェクトから呼び出される特殊な関数です。

以下は、コピーコンストラクタを使用してオブジェクトをコピーする方法の例です。

#include <iostream>
using namespace std;

class MyClass {
  public:
    int x;
    MyClass(int val) : x(val) {}
    MyClass(const MyClass &obj) : x(obj.x) {}
};

int main() {
  MyClass obj1(10);
  MyClass obj2 = obj1; // コピーコンストラクタが呼び出される
  cout << "obj1.x: " << obj1.x << endl;
  cout << "obj2.x: " << obj2.x << endl;
  return 0;
}

上記の例では、MyClassという名前のクラスが定義されています。このクラスには、整数値xを保持するメンバ変数があります。

また、MyClassには引数を取る通常のコンストラクタと、同じ型のオブジェクトから呼び出されるコピーコンストラクタが定義されています。

main関数では、まずobj1という名前でMyClassオブジェクトが作成されます。

次に、obj2obj1を代入する際にコピーコンストラクタが呼び出されて、obj1の中身が複製されます。

最後に、両方のオブジェクトのxメンバ変数が表示されます。

単純にコピーするだけでなく、コピーしたことを示すフラグ変数を用意して、コピーコンストラクタ内で設定したりすることも可能です。

コピーコンストラクタの注意点

コピーコンストラクタを使用する際には、いくつかの注意点があります。

まず、コピーコンストラクタはオブジェクトのコピーを作成するために使用されますが、ポインター変数などの参照型メンバー変数を持つ場合には注意が必要です。

例えば以下のようなクラスがあったとします。

class MyClass {
public:
    int* ptr;
    MyClass(int val) {
        ptr = new int(val);
    }
    ~MyClass() {
        delete ptr;
    }
};

このクラスでは、int型のポインター変数ptrを持っています。そして、コンストラクタでnew演算子を使って動的にメモリを確保しています。

この場合、単純にコピーコンストラクタでオブジェクトをコピーしただけでは問題が発生します。

なぜなら、ポインタ変数が保持しているのは変数の実体にアクセスするためのアドレスであるため、単純にa = bのように書いてしまうと、同じデータを参照することになってしまいます。

そのため、元のオブジェクトが破棄されるときにdelete演算子で解放されてしまうと、新しいオブジェクトでも同じメモリ領域を指しているため不正アクセスエラーが発生します。

このような場合にはディープコピー(深い複製)を行う必要があります。

具体的には、自分自身で新しいメモリ領域を確保して値をコピーする必要があります。以下はディープコピーを行う方法です。

MyClass(const MyClass& obj) {
    ptr = new int(*obj.ptr);
}

上記のように書くことで、新しいオブジェクトも別々のメモリ領域を指すようになります。

また、もう一つ注意点としては継承関係にある場合です。

基底クラスと派生クラス間でコピーコンストラクタや代入演算子(=)を定義する場合は、「基底部分」も適切に処理しなければなりません。

具体的には、「基底部分」も適切なコピーコンストラクタや代入演算子(=)を呼び出す必要がありますので注意しましょう。

1 2

この記事のページ一覧
  1. 現在のページ
目次から探す