C++の仮想コピーコンストラクタとは?clone()とcreate()の仕組みを実例で解説
本題に入る前に、まずこの記事に関連する重要な用語を確認しておきましょう。
前提知識の整理
コピーコンストラクタとは
コピーコンストラクタとは、渡されたオブジェクトと全く同じ内容を持つ新しいオブジェクトを生成するための特殊なコンストラクタです。同じクラスの別オブジェクトを引数に受け取り、そのメンバ変数の値を複製します。
仮想関数(virtual関数)とは
仮想関数とは、基底クラス(親クラス)で宣言され、その基底クラスを継承した派生クラス(子クラス)で再定義(オーバーライド)されるメンバ関数のことです。仮想関数を使うことで、ポインタ経由でも実際のオブジェクトの型に応じた関数が呼び出されるようになります。
仮想コピーコンストラクタが必要な理由
C++では、コピーコンストラクタは既存のオブジェクトを元に新しいオブジェクトを生成するために使われます。しかし、実行時(ランタイム)に初めて生成すべきオブジェクトの型が決まる場合、通常のコピーコンストラクタだけでは対応できません。たとえば、ユーザーが入力した値や特定の条件に基づいて、コンパイル時ではなく実行時にオブジェクトの型が決定するようなケースです。
このような状況で必要になるのが「特別な力を持ったコピーコンストラクタ」、すなわち仮想コピーコンストラクタです。これにより、プログラマはオブジェクトの正確なデータ型を事前に知らなくても、実行時にオブジェクトを生成・複製できるようになります。
具体例:図形の面積計算
例として、図形の面積を求めるプログラムを考えてみましょう。オブジェクトの型は実行時に決定し、それが正方形なのか、長方形なのか、円なのかは事前には分かりません。そこで仮想コピーコンストラクタを使えば、ユーザーが入力した種類に応じて、適切な図形オブジェクトを動的にコピー・生成できます。
仮想コンストラクタを実現する2つのメソッド
仮想コンストラクタを正しく機能させるためには、基底クラス上に次の2つのメソッドを定義します。
clone()
create()
- clone():コピーコンストラクタと組み合わせて使用され、既存オブジェクトの複製を作ります。
- create():デフォルトコンストラクタと組み合わせて使用され、仮想コンストラクタとして新しいオブジェクトを生成します。
サンプルコード
以下は、図形クラス(figure)を基底クラスとし、正方形(square)、円(circle)、長方形(rectangle)を派生クラスとして実装した例です。
#include <iostream>
using namespace std;
class figure {
public:
figure() { }
virtual ~figure() { }
virtual void ChangeAttributes() = 0;
static figure *Create(int id);
virtual figure *Clone() = 0;
};
class square : public figure {
public:
square() {
cout << "square created" << endl;
}
square(const square& rhs) { }
~square() { }
void ChangeAttributes() {
int a;
cout << "The side of square";
cin >> a;
cout << "Area of square is " << a * a;
}
figure *Clone() {
return new square(*this);
}
};
class circle : public figure {
public:
circle() {
cout << "circle created" << endl;
}
circle(const circle& rhs) { }
~circle() { }
void ChangeAttributes() {
int r;
cout << "enter the radius of the circle ";
cin >> r;
cout << "the area of circle is " << ((3.14) * r * r);
}
figure *Clone() {
return new circle(*this);
}
};
class rectangle : public figure {
public:
rectangle() {
cout << "rectangle created" << endl;
}
rectangle(const rectangle& rhs) { }
~rectangle() { }
void ChangeAttributes() {
int a, b;
cout << "The dimensions of rectangle ";
cin >> a >> b;
cout << "Area of rectangle is " << a * b;
}
figure *Clone() {
return new rectangle(*this);
}
};
figure *figure::Create(int id) {
if (id == 1) {
return new square;
}
else if (id == 2) {
return new circle;
}
else {
return new rectangle;
}
}
class User {
public:
User() : figures(0) {
int input;
cout << "Enter ID (1, 2 or 3): ";
cin >> input;
while ((input != 1) && (input != 2) && (input != 3)) {
cout << "Enter ID (1, 2 or 3 only): ";
cin >> input;
}
figures = figure::Create(input);
}
~User() {
if (figures) {
delete figures;
figures = 0;
}
}
void Action() {
figure *newfigure = figures->Clone();
newfigure->ChangeAttributes();
delete newfigure;
}
private:
figure *figures;
};
int main() {
User *user = new User();
user->Action();
delete user;
}
コードのポイント
- 基底クラス figure は純粋仮想関数
ChangeAttributes()とClone()を持ち、抽象クラスとして機能します。 - 静的メソッド
Create(int id)が、入力されたIDに応じて対応する派生クラスのインスタンスを生成します(ファクトリメソッドの役割)。 - 各派生クラスの
Clone()は、自分自身の型を認識しており、自分と同じ型のオブジェクトをコピーして返します。これが仮想的なコピーコンストラクタの働きです。 - User クラスは
Action()メソッド内でClone()を呼び出すことで、元のオブジェクトの型を意識せずに複製を作り、属性変更を行っています。
実行結果
Enter ID (1, 2 or 3): 2
circle created
enter the radius of the circle R 3
the area of circle is 28.26
この出力例では、ユーザーがID「2」を入力したため、実行時に circle(円)オブジェクトが生成され、半径3の円の面積(28.26)が計算されています。このように、仮想コピーコンストラクタの仕組みを使えば、オブジェクトの具体的な型をコンパイル時に確定させることなく、柔軟なオブジェクト生成・複製が可能になります。
-
C++のコピーコンストラクタと代入演算子の違いを徹底解説
C++におけるコピーコンストラクタと代入演算子(コピー代入演算子)は、どちらも「あるオブジェクトを別のオブジェクトで初期化・設定する」ために使用されます。しかし、両者には重要な違いがあります。最大の相違点はメモリの扱いです。コピーコンストラクタは新しいオブジェクトに対して独立したメモリ領域を確保します。一方、代入演算子は新しいメモリ領域を確保せず、既存のオブジェクトに値を上書きする形になります。つまり、すでに存在するメモリブロックに対して操作が行われる点が特徴です。コピーコンストラクタの構文classname (const classname &obj) { // コンストラクタの本体
-
C++のコピーコンストラクタとは?仕組みと実装方法をサンプルコード付きで解説
コピーコンストラクタは、コンストラクタの一種です。同じクラスのオブジェクトを受け取り、その値を使って新しいオブジェクトを生成・初期化します。もしクラス内でコピーコンストラクタが定義されていない場合、コンパイラが自動的にデフォルトのコピーコンストラクタを生成します。ただし、ポインタ変数や動的メモリ確保(new/delete)を持つクラスでは、デフォルトのコピーは浅いコピー(shallow copy)となり、同じメモリ領域を複数のオブジェクトが参照してしまう問題が発生します。そのため、このようなクラスでは独自のコピーコンストラクタを定義することが必須となります。コピーコンストラクタのサンプルプログ