こんにちは、C++の案件を年に1度だけ対応するプログラマーDENです。
今回は変数の初期化について。
Javaプログラマーの僕がなんで?って思った事について調べてみました。
スポンサーリンク
変数を初期化しないのに動く?
C++案件を修正していると、何も値を代入していない変数を平然と使っているソースコードを見かけます。
こんな感じです。
int _tmain(int argc, _TCHAR* argv[]) { Foge foge; std::cout << foge.int_a; }
この変数fogeには、値が何も代入されていません。
インスタンスのない変数のメンバであるint_aを参照しています。
Javaだったらこれはコンパイルエラーになります。
ですがC++でコンパイルも通りますし、普通に動いちゃうんですよ。
不思議ですよね。
では変数fogeには何が入っているのでしょうか…?
暗黙的に呼び出されるデフォルトコンストラクタ
実は、C++では値の代入を省略すると、デフォルトコンストラクタが暗黙的に呼び出されるのです。
デフォルトコンストラクタの性質
- 引数の指定がないコンストラクタ
- コンストラクタが1つも定義されていなければ自動生成される
暗黙的に、引数なしのコンストラクタが呼ばれるという事なので、下記の2つのソースコードはイコールになります。
Foge foge;
Foge foge = Foge();
なるほど、変数の中身はかっらぽかと思っていましたが、暗黙的にインスタンスが代入されていたのでコンパイルエラーにもならずに、正常に動作していたんですね〜。
暗黙的代入を利用する際の注意点
じゃあ、引数ありのコンストラクタを使う時以外は初期値を入れる必要ないね!
って思うかもしれませんが、ちょっと待ってください。
下記のようなケースはエラーになってしまうので注意です。
基本型は暗黙的代入の対象外
下記のコードはエラーになります。
int a; std::cout << a;
基本型は暗黙的に初期化されません。
引数なしコンストラクタがないクラスは暗黙的代入の対象外
こちらのケースもエラーになります。
class Foge { public: int int_a; Foge(int param){ int_a = param; } }; int _tmain(int argc, _TCHAR* argv[]) { Foge foge; std::cout << foge.int_a; return 0; }
基本型でもないので、デフォルトコンストラクタが呼び出せれると思いきや、このクラスはデフォルトコンストラクタがないんです。
コンストラクタが1つも実装されていなければ、デフォルトコンストラクタが自動生成されるのですが、1つでも実装されていれば生成されません。
このFogeクラスは、Foge(int param)の引数ありコンストラクタしか存在しないのです。
デフォルトコンストラクタが存在しないので、デフォルトコンストラクタを呼び出す暗黙的代入が機能しないということですね。
下記のソースコードは正常に動きます。
class Foge { public: int int_a; }; int _tmain(int argc, _TCHAR* argv[]) { Foge foge; foge.int_a = 1; std::cout << foge.int_a; return 0; }
このコードでは、Fogeクラスにコンストラクタが何も実装されていませんので自動的にデフォルトコンストラクタ生成されます。
デフォルトコンストラクタがあれば、暗黙的代入が作動するので問題なく動くという事です。
まとめ
C++で何も値を代入していない変数は、一見空のように見えますが、暗黙的にデフォルトコンストラクタで生成されたオブジェクトが代入されているんですね。
ただし、暗黙的に値が代入されるには条件があり、一概にC++で初期化不要と言えるわけではありません。
ソースコードの可読性や、後から引数ありコンストラクタを定義したら動作しなくなるリスクを考えると、暗黙的代入に頼らず変数は初期化する癖をつけておいたほうが良さそうですね。