ホームに戻る
出典 :
関連 :
目次 :
変換コンストラクタ
あるクラスにおいて、コンストラクタの引数がひとつだけの場合 = でコンストラクタを呼び出すことができる。
class SimpleClass
{
private:
int number;
public:
// デフォルトコンストラクタ
SimpleClass() { number = 0; }
// 引数がひとつのコンストラクタ ⇒ 変換コンストラクタ
SimpleClass(int n) { number = n; }
int get() { return number; }
void set(int n) { number = n; }
};
int main()
{
// 変換コンストラクタの呼び出し
SimpleClass sc1 = 15;
// 変換コンストラクタの呼び出し
SimpleClass sc2;
sc2 = 20;
std::cout << sc1.get() << std::endl; //< 15
std::cout << sc2.get() << std::endl; //< 20
}
クラス SimpleClass における SimpleClass(int n) は引数をひとつだけ取るコンストラクタであり、変換コンストラクタとして動作する。
値を代入(に見える操作を)することで、変換コンストラクタを呼び出すことができるが、
このとき、与えられた値の暗黙的な変換が可能な場合のみ変換コンストラクタが呼び出され、オブジェクトが生成される。
(上記の場合、= の右辺値である 15 や 20 は int に変換が可能なため変換コンストラクタが呼び出されるが、
右辺値が "abc" など int に変換できない値の場合はコンパイルエラーとなる。)
変換コンストラクタとなる条件は「引数ひとつで呼び出せる」ことのみであるため、以下のコードのコンストラクタはすべて変換コンストラクタとして機能する。
(第1引数以外がすべてデフォルト引数となっているものも含まれる。)
class SimpleClass
{
private:
int integer;
double real;
std::string str;
public:
// 以下はすべて変換コンストラクタ
SimpleClass(double x) { real = x; }
SimpleClass(char* x) { str = x; }
SimpleClass(int x, double y = 0.0)
{
integer = x;
real = y;
}
};
int main()
{
// SimpleClass(double)の呼び出し ⇒ OK
SimpleClass sc1 = 3.0;
// SimpleClass(char*)の呼び出し ⇒ OK
SimpleClass sc2 = "Hello";
// SimpleClass(int, double)の呼び出し ⇒ OK
// (第1引数のみを指定)
SimpleClass sc3 = 7;
// SimpleClass(int, double)の呼び出し ⇒ NG
// (第1引数以外も指定)
// SimpleClass sc4 = (10, 4.5);
// 通常のコンストラクタ呼び出し ⇒ OK
SimpleClass sc4(10, 4.5);
}
暗黙的呼び出しの禁止(explicit)
変換コンストラクタが使用できることで、問題となるケースが存在する。
class SimpleClass
{
private:
int integer;
public:
SimpleClass(int x) { integer = x; }
};
void func(SimpleClass c)
{
(略)
}
int main()
{
// エラーにならない ⇒ 変換コンストラクタが動作
func(10);
}
上記コードの関数 func() は SimpleClass 型の引数を想定している。しかし main() 中の func() 呼び出しでは、引数の 10 が SimpleClass のコンストラクタ引数と解釈されてしまう。
変換コンストラクタを禁止することで、この問題を防ぐことができる。
class SimpleClass
{
private:
int integer;
public:
// 暗黙的呼び出し(変換コンストラクタ)の禁止
explicit SimpleClass(int x) { integer = x; }
};
void func(SimpleClass c)
{
(略)
}
int main()
{
// 暗黙的呼び出しのためエラー
// func(10);
// 明示的にコンストラクタを呼んでいるためOK
SimpleClass sc(10);
func(sc);
// キャストは明示的呼び出しと見なされるためOK
func( (SimpleClass)10 );
}
コンストラクタを explicit 修飾することで、暗黙的なコンストラクタ呼び出し( = 変換コンストラクタ)を禁止できる。
変換コンストラクタは混乱の元となることも多く、禁止しておくことが有効な選択となり得る。