ATMクラスの時にコンストラクタ関数の説明をしました。
ここでは、もう少し詳しく説明します。
コンストラクタ関数は、クラスが実体を持った時に自動的に呼び出されます。
では、実体を持った時とはいつでしょうか?
サンプルを作って確認してみます。
<sample program cpp052-01>
#include <iostream> class Player { public: Player(); }; int main() { std::cout << "main関数の開始" << std::endl; Player player; std::cout << "main関数の終了" << std::endl; return 0; } Player::Player() { std::cout << "Playerクラスが実体を持ちました。" << std::endl; } |
<実行結果>
main関数の開始 Playerクラスが実体を持ちました。 main関数の終了 続行するには何かキーを押してください・・・
実行した時のメッセージを見ても分かる通り、
Player player; |
で、実体が作られています。
実体が作られるとは、「メモリ上にデータが配置される」事を指します。
例えば、次の例を見てください。
<sample program cpp052-02>
#include <iostream> class Player { public: Player(); }; int main() { std::cout << "main関数の開始" << std::endl; Player *pPlayer; std::cout << "main関数の途中" << std::endl; pPlayer = new Player; std::cout << "main関数の終了" << std::endl; delete pPlayer; return 0; } Player::Player() { std::cout << "Playerクラスが実体を持ちました。" << std::endl; } |
<実行結果>
main関数の開始 main関数の途中 Playerクラスが実体を持ちました。 main関数の終了 続行するには何かキーを押してください・・・
クラスのポインタ変数を作った時にはメッセージは出ませんが、newで実体を持った時にメッセージが表示されています。
コンストラクタ関数には戻り値はありませんが、引数は設定する事が出来ます。
初期化リストと合わせると↓のように使えます。
<sample program cpp052-03>
#include <iostream> class Player { public: Player(const int hp); private: int m_hp; }; int main() { return 0; } Player::Player(const int hp) : m_hp(hp) { } |
コンストラクタ関数が受け取った引数を、そのまま初期化リストでメンバ変数にセットしています。
コンストラクタ関数はクラスが実体を持った時に呼び出されますから、その時に引数を渡さなければなりません。
では、呼び出しと確認を追加して動かしてみましょう。
<sample program cpp052-04>
#include <iostream> class Player { public: Player(const int hp); void ShowStatus() const; private: int m_hp; }; int main() { Player player(100); player.ShowStatus(); return 0; } Player::Player(const int hp) : m_hp(hp) { } void Player::ShowStatus() const { std::cout << "HP = " << m_hp << std::endl; } |
<実行結果>
HP = 100 続行するには何かキーを押してください・・・
コンストラクタ関数に引数を渡すには、
Player player(100); |
実体を作ると同時に渡します。
上のプログラムをちょっとだけ変えてコンパイルしてみましょう。
<sample program cpp052-05>
#include <iostream>
class Player {
public:
Player(const int hp);
void ShowStatus() const;
private:
int m_hp;
};
int main()
{
Player player;
player.ShowStatus();
return 0;
}
Player::Player(const int hp) : m_hp(hp)
{
}
void Player::ShowStatus() const
{
std::cout << "HP = " << m_hp << std::endl;
}
|
<コンパイル結果>
error C2512: 'Player': クラス、構造体、共用体に既定のコンストラクターがありません。 note: 'Player' の宣言を確認してください
引数を渡さないで実体を作ろうとしたところエラーになりました。
引数を持つコンストラクタ関数を作ると、クラスの実体を作る時には引数が必須になります。
エラーメッセージに「既定のコンストラクター」という言葉があります。
もし、プログラマがコンストラクタ関数を作っていない場合、自動的に「引数無し」のコンストラクタ関数(既定のコンストラクタ)が作られます。
引数を持つコンストラクタ関数を作った場合、既定のコンストラクタは作られません。
引数を持ったコンストラクタ関数は必要だが、引数無しでもクラスを実体化させたい!という場合は次のようにします。
コンストラクタも関数なので、オーバーロード(多重定義)させる事が出来ます。
<sample program cpp052-06>
#include <iostream> class Player { public: Player(); Player(const int hp); void ShowStatus() const; private: int m_hp; }; int main() { Player player; player.ShowStatus(); return 0; } Player::Player() : m_hp(150) { } Player::Player(const int hp) : m_hp(hp) { } void Player::ShowStatus() const { std::cout << "HP = " << m_hp << std::endl; } |
<実行結果>
HP = 150 続行するには何かキーを押してください・・・
引数の数や型が異なれば同じ名前の関数を作れます。
これで、引数無し、引数ありの両方に対応するコンストラクタ関数が出来ました。
最後にコピーコンストラクタについて説明します。
コピーコンストラクタとは、すでにあるクラスの実体をコピーして新しいクラスを作るためのコンストラクタ関数です。
上で作ったPlayerクラスを元にサンプルを作りましょう。
<sample program cpp052-07>
#include <iostream>
class Player {
public:
Player();
void ShowStatus() const;
private:
int m_hp;
};
int main()
{
Player player;
Player partner(player);
partner.ShowStatus();
return 0;
}
Player::Player() : m_hp(150)
{
}
void Player::ShowStatus() const
{
std::cout << "HP = " << m_hp << std::endl;
}
|
<実行結果>
HP = 150 続行するには何かキーを押してください・・・
ポイントは、
Player partner(player); |
です。
partnerのコンストラクタの引数として、すでに作ってあったplayerをセットしています。
ただ、特別なコンストラクタ関数をクラスに追加した形跡はありません。
実は、コピーコンストラクタも自動的に作られる関数なのです。
もちろん、自分で作り直す事も出来ますので、やってみましょう。
<sample program cpp052-08>
#include <iostream> class Player { public: Player(); Player(const Player &source); void ShowStatus() const; private: int m_hp; }; int main() { Player player; Player partner(player); partner.ShowStatus(); return 0; } Player::Player() : m_hp(150) { } Player::Player(const Player &source) { std::cout << "自作のコピーコンストラクタ" << std::endl; } void Player::ShowStatus() const { std::cout << "HP = " << m_hp << std::endl; } |
<実行結果>
自作のコピーコンストラクタ HP = -858993460 続行するには何かキーを押してください・・・
コピーコンストラクタのプロトタイプ宣言を見てみましょう。
Player(const Player &source); |
引数は「自分のクラスのconst参照」です。
本体を見ると、
Player::Player(const Player &source) { std::cout << "自作のコピーコンストラクタ" << std::endl; } |
メッセージを表示するように作ってあります。
しかし、実行結果はメンバ変数のm_hpのコピーはされていません。
当然、自作したのですから自分でコピーしなければなりません。
とりあえず、本体を次のようにして実行してみてください。
Player::Player(const Player &source)
{
std::cout << "自作のコピーコンストラクタ" << std::endl;
m_hp = source.m_hp;
}
|
<実行結果>
自作のコピーコンストラクタ HP = 150 続行するには何かキーを押してください・・・
上手くいったようです。
が、この方法だとメンバ変数が大量にある場合に面倒になりそうです。
その対処法はコラム「thisポインタ」で説明します。