変数の宣言と定義について

C++の勉強(4)

前回のつづき.変数の「宣言」と「定義」について.

具体的な問題の詳細と正しい実装方法

全てのインスタンスで共通の列挙型変数enum Character {Cheerful, Gloomy}をコンストラクタに与え,それによってインスタンスされたオブジェクトの状態が変わるようにしたかった.この場合には,この列挙型変数によってPersonオブジェクトの性格が決まることになる.そこで,Person.hppで宣言されたクラスPersonインスタンス化する際に,main.cppに列挙型変数Characterの値を指定したい.これには,Person.hppのコストラクタを次のように定義すれば良い.

Person(Character character):_Char(character){
}

これにより,メンバー変数_Charがコンストラクタに与えられた引数で初期化され,あるPersonオブジェクトの性格が固定される.また,_Charの値によってメンバ関数void Talk_To()の反応が変わるようにPersonクラスを改良すると以下のようになる.

性格によって話しかけた時の反応が変わるヘッダ

性格によって話しかけたときの反応が変わるソース

これで,main関数を次のようにする.

//main.cpp
#include "Person.hpp"
int main(){
  Person *Anthony = new Person(Cheerful);
  std::cout << "Anthony said, " << std::endl;
  Anthony->Talk_To();

  std::cout << "Ben said, " << std::endl;
  Person *Ben = new Person(Gloomy);
  Ben->Talk_To();

  delete Anthony;
  delete Ben;
  return 0;
}

コンパイル・実行すると次の結果になる.

Anthony said,
Hey bro!
Ben said,
Ah... Hi...

Personクラスの一つ目のオブジェクトであるAnthonyは性格_Charが明るいCheerful陽キャなので元気に反応するが,Benは暗いGloomyなので反応も暗い.

うまくコンパイルができなかった原因

はじめに,「宣言」「定義」の違いがわからなかったことが原因でコンパイルできなかったと言ったが,それはenumの仕様を理解していなかったことも原因の一つだった.前回の記事に書いたように,Person.hppの最初にあるenum Character {Cheerful, Gloomy}「宣言」である.このため,ファイル分割の規則(宣言はヘッダファイルに,定義はソースファイルに)に則ってヘッダファイルに書いている.しかし,使い始めた当初は「宣言」「定義」の違いがわからずに,とりあえずいろいろなファイルに列挙型変数Characterの存在を知らしめたかったので,extern enum Character {Cheerful, Gloomy}としていた.が,externはある変数の「定義」を外部のファイルにも知らしめるたものもので,「宣言」であるenum ...に対して使えないからコンパイルエラーがでていたのである.これを修正した上のPersonクラスは仕様どおりに正常に動く.

まとめ

  • enum 型名 {値1, 値2, ...} 変数名で列挙型型名を宣言し,変数名を定義することができる.
  • ファイル分割の際にはヘッダファイルに宣言を書き,ソースファイルに定義を書くと見通しが良い.
  • main.cppAnthony, Bendeleteしなくてはならなかったのが心苦しかった.