Version 16.11
様々なコンストラクタ
「前回はコンストラクタについて簡単に復習しました」
『ただの普通のコンストラクタね』
「というわけで、今回は特殊なコンストラクタについて説明します」
『げげ』
「特殊なコンストラクタには、以下の3つがあります」
・デフォルトコンストラクタ
・引数を持つコンストラクタ
・コピーコンストラクタ
「ひとつずつ見ていきます。まずはデフォルトコンストラクタから」
・デフォルトコンストラクタ
「デフォルトコンストラクタは、コンストラクタを作らなくても初めから存
在するコンストラクタです」
『 Version 7.08 ( No.128 ) で教えてもらったのだね』
「たとえば、以下のようにコンストラクタがなくても」
// CDataクラス。
class CData
{
private:
// private メンバ変数。
int m_iData;
};
「デフォルトコンストラクタというコンストラクタが裏で作られて、この
クラスの変数が作られたときに呼び出されます」
// CData クラスの変数を宣言します。
// ここでデフォルトコンストラクタが呼びだされます。
CData cData;
「まぁ、デフォルトコンストラクタは本当に何もしないんだけど」
『? なら、デフォルトコンストラクタがある、って考えない方がいいん
じゃない?』
「ところがそうはいかないんです。たとえば、引数を持つコンストラクタが
ある場合」
・引数を持つコンストラクタ
「コンストラクタは関数の一種なので、引数を持つことができます」
『 Version 11.18 ( No.218 ) で作ったのだね』
// Data.h
// CDataクラス。
class CData
{
private:
// private メンバ変数。
int m_iData;
public:
// コンストラクタ。
CData( int p_i );
};
// Data.cpp
// コンストラクタ。
CData::CData( int p_i )
: m_iData( p_i )
{
}
「このようにコンストラクタに引数を持たせると、クラスの初期化時に関数
のようにして呼び出すことができます」
CData cData( 100 );
「さて、話を戻すと、実は引数を持つコンストラクタを作ると、デフォルト
コンストラクタが消えてしまうんです」
『ええっ!?』
CData cData;
// コンパイルエラー:
// error C2512: 'CData' :
// クラス、構造体、共用体にデフォルト コンストラクタがありません。
『うわー、さっきまでできてたのに……』
「引数付きのコンストラクタがあるときには、引数なしコンストラクタも作
るか、引数付きコンストラクタだけ使うようにする必要があるんです」
『引数なしが、デフォルトコンストラクタの代わりになる……ってこと?』
「そういうこと。デフォルトコンストラクタと引数なしコンストラクタは同
じもの。デフォルトコンストラクタは自動的に作られるってだけだから」
『んー、でもさ、なんでこんな面倒なの? デフォルトコンストラクタが使
えなくなる理由は?』
「安全のため。コンストラクタの目的は?」
『メンバ変数の初期化!』
「だよね。だから、すべてのコンストラクタでちゃんと初期化しなきゃいけ
ないでしょ。初期化をしていないコンストラクタがあったら」
『メンバ変数が初期化されないでクラスが使われちゃう……あ、それを防ぐ
ために、デフォルトコンストラクタがなくなるってこと?』
「そういうこと。そもそもデフォルトコンストラクタはメンバ変数をほった
らかしにするから使わない方がいいくらい。引数なしコンストラクタを作る
癖を付けた方がいいかもね」
『じゃあなんでデフォルトコンストラクタがあるの?』
「それがないと色々と困るから……クラスが構造体と似ているっていうのは」
『説明されました! Version 7.07 ( No.127 ) でやったね』
「もしデフォルトコンストラクタがないと、構造体にも必ずコンストラクタ
が必要になるから」
『? 作ればいいじゃん』
「構造体って、 C言語の頃、つまりずっと昔からあるんだけど、そうなると
昔のプログラムが全部使えなくなっちゃうから」
『あー……そりゃまずいね』
「だからデフォルトコンストラクタは必要なわけ」
『なるほどね……』
「さて、最後にコピーコンストラクタについて」
・コピーコンストラクタ
『これは今回初だね』
「コピーコンストラクタは、デフォルトコンストラクタと同じく、自動的に
作られるコンストラクタです」
『デフォルトコンストラクタの他にもあったんだ』
「たとえば、次のようにメンバ変数のみのクラスがある場合」
// CDataクラス。
class CData
{
public:
// private メンバ変数。
int m_iData;
};
「クラスは、初期化するときに引数として同じクラスを渡すことができるん
です」
『同じクラス、ってことは CData クラスってこと?』
「そういうこと」
// 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 );
// 出力します。
char pch[256];
sprintf( pch, "%d\n", cData2.m_iData );
OutputDebugString( pch );
// 100
return 0;
}
「 CData クラスの変数を作る際に、コンストラクタの引数として同じクラス
の変数を渡すことができるんです」
// もうひとつ CData クラスの変数を宣言します。
// コピーコンストラクタでコピーします。
CData cData2( cData );
「これが、コピーコンストラクタ」
『コピーをするためのコンストラクタだから、コピーコンストラクタね』
「そういうこと。文字通りメンバ変数もコピーしてくれるんです」
『あ、ホントだ。 cData2.m_iData が 100 になってる』
「つまり、クラスにはコピーコンストラクタという見えないコンストラクタ
が用意されていて、そのコピーコンストラクタが自クラス同士のコピーを
行う、というわけです」
『質問!』
「はい火美ちゃん」
『このコピーコンストラクタも、引数付きのコンストラクタがあるとなく
なっちゃう?』
「ううん、これはなくならないよ。コピーコンストラクタは、同じクラスの
メンバ変数をコピーするんだから、ちゃんと初期化をするんです」
『なるほど、自分自身だったら全部のメンバ変数あるもんね。ん? もひと
つ質問!』
「はい火美ちゃん」
『じゃあさ、コピーコンストラクタがある、って考える必要あるの? クラス
はクラス同士でコピーできる、ってことでいいんじゃない?』
「そうはいかないんです。コピーコンストラクタはデフォルトコンストラクタ
と違って、必ずしも自動的に作られるわけではないんです」
『どゆこと?』
「自分で、コピーコンストラクタを作ることができるってこと」
『あー、デフォルトコンストラクタの代わりに引数なしコンストラクタを作
るようなもんね』
「そういうこと。というわけで次回に続く!」
/*
Preview Next Story!
*/
『コピーコンストラクタ……を作る???』
「そう、コピーコンストラクタを作るんです?」
『でも引数は? そもそも存在してるのかどうかもわからないのに!』
「そこが難しい所だね」
『難しいの!?』
「いや、そうでもないけど」
『どっちかはっきりしろー!』
「というわけで次回」
< Version 16.12 コピーコンストラクタを作る >
『につづく!』
「あ、 const 参照使うから」
『難しいじゃんー!!』