注意
はじめにことわっておくがこれは備忘録かつ僕の記憶の定着のための記事だ。
という事で気軽に流して欲しい(が、間違いがあれば教えていただけると大変嬉しい)
早速本題
さて、数日前まで恥ずかしながら変換コンストラクタという言葉を僕は知らなかった。使っている癖にだ。
いわゆる変換コンストラクタとは、引数が一つだけのコンストラクタをいう
#include<string> class Hoge { public: Hoge();//デフォルトコンストラクタ(変換コンストラクタではない) Hoge(int num);//変換コンストラクタ Hoge(std::string str);//変換コンストラクタ Hoge(int a, int b);//引数が2つのコンストラクタ。変換コンストラクタではない。 Hoge& operator =(int num);//=演算子のオーバーロード Hoge& operator =(std::string str);//=演算子のオーバーロード };
という感じ(上から2つめと3つめ)。
これがどうしたの、という感じだけれど、C++ではこやつらは特別な扱いを受けている。
例を見ていこう。
//上のHogeクラスは宣言済みとする int main() { Hoge obj(3); return 0; }
これはまさにHoge::Hoge(int num);
を呼んでいるのはわかるだろう。その上で次を見て欲しい。
//Hogeクラスは宣言済みとする int main() { //Hoge obj(3);と同じ Hoge obj = 3; return 0; }
一見すると=演算子をオーバーロードしたものが呼ばれているように見えるかもしれないが、これは実はコメントにもあるようにHoge obj(3);
と書かれているのと同じであり、つまりはHoge::Hoge(int num);
が呼ばれている(operator =(int num);
*1ではない事に注意)
では、以下の場合を見て欲しい
//Hogeクラスは宣言済みとする int main() { Hoge obj; obj = 3; return 0; }
この場合のobj=3;
では、何が呼ばれているだろうか?これはHoge::Hoge(int num);
ではなく、operator =(int num);
*2だ。
だが、上記までと=
を使っているという所では変わらない。何が違うのだろうか?
同じ「=」でも…
非常に簡単に言うと、初期化か、代入かが違う。
C++では代入と初期化は明確に区別されている。
この違いを非常に簡単に(誤解を恐れず)書くと、とある型のオブジェクトの宣言時にその値を与える形で定義してしまう際に行われるのが初期化であり、既に生成された後のオブジェクトに値を入れるような処理をするのが代入だ。まあ多分コードを見た方が早い。
int a=5;//これが初期化 int b; b=5;//これが代入
その上で言うと、operator
による=の演算子オーバーロードは代入演算子を定義している。初期化の=は定義していない。では、初期化の=は一体何なのだろうか。さっきも書いたのでわかると思うが、これが変換コンストラクタの実装として定義されているものだ。
つまりはまとめると、変換コンストラクタに於いては、MyClass(type val);
という変換コンストラクタがある場合に、MyClass obj(value);
と書くと変換コンストラクタが明示的に呼べるし(value
はtype
型の値)、MyClass obj=value;
と書くことで暗黙的に呼ぶことも出来る。つまり
//Hogeクラスは宣言済みとする int main() { Hoge obj(3);//Hoge(int)が呼ばれた Hoge obj2 = 3;//Hoge(int)が呼ばれた obj = 5;//operator =(int)が呼ばれた obj2 = 7;//operator =(int)が呼ばれた return 0; }
となるわけである。もし、変換コンストラクタによる初期化を=で出来ないようにしたければ、その変換コンストラクタにexplicit
キーワードをつければ良い
この辺がわかっていると組み込み型との差を意識しないクラスの実装などをするのが楽なので非常に便利だと思う。
参考ページ
以下、参考にさせていただいたページ。そもそも変換コンストラクタについて教えてくださったいかろちゃん先輩(混乱)には感謝しています。