Version 16.12
コピーコンストラクタを作る
「今回は、前回説明したコピーコンストラクタの作り方について説明します」
『メンバ変数の中身をコピーしてくれるコンストラクタなんだよね』
「そうなんだけど、コピーコンストラクタを作ったら、そのコピーの処理は
自分で書かなきゃいけないんです」
『げ、それ面倒』
「だから、単にコピーするだけならコピーコンストラクタを作る必要はない
かな。コピー時に特別な処理が必要な時だけコピーコンストラクタを作る、
っていうのがいいかな」
『りょーかい』
「さて、では実際にコピーコンストラクタを作ってみます。まずクラスの
宣言の側」
// Data.h
// CDataクラス。
class CData
{
public:
// private メンバ変数。
int m_iData;
public:
// 引数のないコンストラクタ。
CData();
// コピーコンストラクタ。
CData( const CData &p_rcData );
};
『あれ? コピーコンストラクタって…… CData クラスの、const 参照な
んだ』
// コピーコンストラクタ。
CData( const CData &p_rcData );
「そう、これは仕様として決められているから変えられません。それに、前
回説明したように、以下のようにして渡せないといけないから」
// もうひとつ CData クラスの変数を宣言します。
// コピーコンストラクタでコピーします。
CData cData2( cData );
『そか、普通に渡せなきゃいけないから自分のクラスの参照になるんだね。
const なのは書き換え不可にするから?』
「そういうこと。コピー元が書き換わっちゃ意味ないからね。次に、この時
呼ばれるコピーコンストラクタの定義の方を見てみます」
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// 引数のないコンストラクタ。
CData:: CData()
: m_iData( 0 )
{
}
// コピーコンストラクタ。
CData:: CData( const CData &p_rcData )
: m_iData( p_rcData.m_iData )
{
// テスト用に出力します。
char pch[256];
sprintf( pch, "CData::CData( const CData & ):%d\n", m_iData );
OutputDebugString( pch );
}
「コピーコンストラクタは以下の部分」
// コピーコンストラクタ。
CData:: CData( const CData &p_rcData )
: m_iData( p_rcData.m_iData )
{
// テスト用に出力します。
char pch[256];
sprintf( pch, "CData::CData( const CData & ):%d\n", m_iData );
OutputDebugString( pch );
}
「この関数の中身は、テスト用に出力している箇所だから本当は必要ないか
らね」
『ってことは、必要なのは』
CData:: CData( const CData &p_rcData )
: m_iData( p_rcData.m_iData )
{
}
『ってことね。引数で自クラスの const 参照受け取って、メンバ変数の
m_iData をコピー……あ、これがコピーしてるとこなんだ』
「そういうこと。これをしないと、コピーコンストラクタが呼ばれているの
にデータがコピーされていない、ってことになるから注意してね」
『はーい』
「あとは引数を持つコンストラクタと同じ。引数が自クラスの const 参照
っていうところだけが他のコンストラクタとの違いだね」
『そう考えると特に難しくないね……あ、質問!』
「はい火美ちゃん」
『引数のないコンストラクタってなんであるの? 省略してもいい?』
「ううん、前回説明したように引数を持つコンストラクタを省略すると」
『あ、デフォルトコンストラクタが消えちゃう!』
「だから、代わりに引数のないコンストラクタを作らなきゃいけないんで
す」
『それはコピーコンストラクタでも同じってことなのね……んー』
「どうしたの?」
『ううんなんでもない。で、使用例はどんな感じ』
「うん、使用例と出力結果は以下のようになります」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// CData クラスの変数を宣言します。
CData cData;
cData.m_iData = 100;
// もうひとつ CData クラスの変数を宣言します。
// コピーコンストラクタでコピーします。
CData cData2( cData );
// CData::CData( const CData & ):100
// 出力します。
char pch[256];
sprintf( pch, "%d\n", cData2.m_iData );
OutputDebugString( pch );
// 100
}
『コピーコンストラクタの使い方は、前回のと同じだね』
// もうひとつ CData クラスの変数を宣言します。
// コピーコンストラクタでコピーします。
CData cData2( cData );
// CData::CData( const CData & ):100
『ちゃんと作ったコンストラクタが呼ばれてるね』
「そこが重要。自動的に作られたコピーコンストラクタと同じように、作成
したコピーコンストラクタがちゃんと呼ばれてるってこと」
『…………』
「? どうしたの?」
『……質問!』
「はい火美ちゃん」
『今さ、その自動的に作られたコピーコンストラクタの代わりに呼ばれたっ
て言ったけど……それってさ、単に、自クラスの const 参照を引数に持つ
コンストラクタが呼ばれた、ってだけじゃないの?』
「あ、そう見えちゃうよね」
『見えちゃうっていうか、そうじゃないの? ってゆーか、もしかして
コピーコンストラクタっていうものが存在しないんじゃないの?』
「そんなことはないよ。たとえば、コピーコンストラクタは以下のようにし
て使うことができるんです」
// もうひとつ CData クラスの変数を宣言します。
// コピーコンストラクタでコピーします。
CData cData2 = cData;
// CData::CData( const CData & ):100
『…… = で渡してる?』
「引数を持つコンストラクタの時に説明したけど」
『あ! そういえば、あのとき = で、引数ひとつのコンストラクタを呼び
出せるって……』
「それと同じ。そのとき、自クラスを渡すとコピーコンストラクタが呼び出
されるんです」
『そして……この = の時も、ちゃんとコピーコンストラクタが呼び出され
てる!』
「そういうこと。まとめると以下のようになります」
・クラス型変数の初期化時に同じクラスの変数を渡すとコピーコンストラクタ
が呼び出される。
・コピーコンストラクタは初めから存在する。
・「自クラスの const 参照」を引数に持つコンストラクタを作ると、それが
コピーコンストラクタになる。
『ふ、複雑……』
「コピーコンストラクタっていう見えないコンストラクタがあって、それを
自クラスの const 参照で作ることができる、っていうところが難しいかも
ね。見えない所だし」
『見えないものは難しいよ……』
/*
Preview Next Story!
*/
『……なんか、この前の const といい、難しくなってきたね……』
「でしょう」
『でしょう、じゃねー!!』
「でももっと難しくなるよ」
『うそー』
「でもその前に復習から」
『なんだ良かったー』
「というわけで次回」
< Version 16.13 オーバーロードの復習 >
『につづく!』
「ほら、甘いものの前に酸っぱいものをって」
『ダメじゃん!』