C++クラスメンバーのコロン初期化とコンストラクション関数内の割り当て

5405 ワード

通常、クラスメンバーの初期化には2つの方法があります.
1.コンストラクション関数の後にコロンを付ける.
2.コンストラクション関数にメンバーを割り当てます.
この小さな細部にあまり注意しない人もいれば、彼らの違いを全然知らない人もいます.2つの方法は同じだと思っています.この誤解は時々プログラムに影響を与える可能性がありますが、ここではこの2つの方法を紹介します.
まず、このコードを見てみましょう.
class A
{
public:
	A(int& c)
	{
		_a = 1;
	}
protected:
	int _a;
	const int _b;
	int& _c;
};

このコードは正しいですか?答えは否定で、このコードはコンパイルできません.次のコンパイルエラーが表示されます
1>d:\study\myconsole\myconsole\myconsole.cpp(14) : error C2758: 'A::_b' : must be initialized in constructor base/member initializer list 1>        d:\study\myconsole\myconsole\myconsole.cpp(20) : see declaration of 'A::_b' 1>d:\study\myconsole\myconsole\myconsole.cpp(14) : error C2758: 'A::_c' : must be initialized in constructor base/member initializer list 1>        d:\study\myconsole\myconsole\myconsole.cpp(21) : see declaration of 'A::_c'はメンバーのことを意味します.bと_cコンストラクション関数のメンバー初期化リストで初期化する必要があります.ではaどうして間違えなかったの?メンバーの声明を見てみましょうaはintタイプです.bはconst intタイプです.cはint&タイプです.C++のルールではconstタイプと参照は付与できず、初期化のみ可能です.ここではconstタイプとリファレンスを少し時間をかけて見てみましょう.
次のコードが正しいかどうか見てみましょう.
int _tmain(int argc, _TCHAR* argv[])
{
	int a;
	const int b;
	int& c;

	return 0;
}

コンパイルすると2つのエラーが表示されます:1>d:studymyconsolemyconsolemyconsole.cpp(30) : error C2734: 'b' : const object must be initialized if not extern 1>d:\study\myconsole\myconsole\myconsole.cpp(31) : error C2530: 'c' : references must be initialized
ああ、constとリファレンスは宣言時に初期化する必要があります(実はconstとリファレンスは変数の作成が完了した後に値を付与できないため、コンパイラはこの制限をしました).OK、コードを変更します.
int _tmain(int argc, _TCHAR* argv[])
{
	int a;
	const int b=5;
	int& c = a;

	return 0;
}

コンパイルに問題はありません.注意深い友达は私がここでbとc宣言コードのところで使用している=号ではなく()を発見することができますが、実は私たちもそうすることができます.
int _tmain(int argc, _TCHAR* argv[])
{
	int a;
	const int b(5);
	int& c(a);

	return 0;
}

この場合、カッコとイコールで初期化し、効果は同じです.具体的には詳しく話さない.
OK、この小さな例を挙げる目的は、constとリファレンスの印象を深めることです.constとリファレンスは宣言時に初期化しなければなりません.言い換えれば、constとリファレンスタイプ変数にメモリを割り当てるときに初期化します.
では、class Aの問題に戻ります.クラスAにはconstメンバーと参照メンバーがあります.システムがクラスAのオブジェクトにメモリを割り当てるとき、システムはAのオブジェクトの3つのメンバーを与える必要があります.a, _b, _cメモリを割り当てます._a問題ありません.システムは直接メモリを与えます._bと_cに問題が発生し、メモリを割り当てるときに初期化されていません.だからコンパイルに問題が発生しました.実はC++がクラスメンバーを初期化する唯一の方法はメンバー初期化リスト、つまり構造関数の後ろにコロンが付いている形式です.class Aのコードを調整します.
class A
{
public:
	A(int& c): _b(2), _c(c)
	{
		_a = 1;
	}
protected:
	int _a;
	const int _b;
	int& _c;
};




int _tmain(int argc, _TCHAR* argv[])
{
	int number = 3;
	A a(number);

	return 0;
}

Aのコンストラクション関数の後にコロンで初期化します.bと_c.コンパイルできます.なぜならシステムはbと_cメモリを割り当てるときに初期化します.コードを次の形式に変更します.
class A
{
public:
	A(int& c)
	{
		_a = 1;
		_b = 2;
		_c = c;
	}
protected:
	int _a;
	const int _b;
	int& _c;

};

これでいいですか.コンパイルすると次のエラーが発生します.
1>d:\study\myconsole\myconsole\myconsole.cpp(14) : error C2758: 'A::_b' : must be initialized in constructor base/member initializer list 1>        d:\study\myconsole\myconsole\myconsole.cpp(22) : see declaration of 'A::_b' 1>d:\study\myconsole\myconsole\myconsole.cpp(14) : error C2758: 'A::_c' : must be initialized in constructor base/member initializer list 1>        d:\study\myconsole\myconsole\myconsole.cpp(23) : see declaration of 'A::_c' 1>d:\study\myconsole\myconsole\myconsole.cpp(17) : error C2166: l-value specifies const object
この3つのエラーには2つの意味が含まれています.
1.constと参照変数は初期化されていません.
2.const変数に対して_bは付与を行い,const変数は左値(error C 2166:l-value specifies const object)としてはならないといえる.
構造関数でイコール番号を呼び出すのは、本当の意味での「初期化」ではないことがわかります.このプロセスは次のようなものです.
1.システムはメンバー変数を作成する;
2.作成後、値付け操作を行います.
コンストラクション関数の後にコロンを付けると、次のようになります.
1.システムはメンバー変数を作成し、初期化します.つまり、システムはメンバー変数にメモリを割り当て、対応するデータを入力します.コンストラクション関数でイコールを呼び出す方法は、割り当てられてから値を割り当てる方法で、1つのステップが増えています.
次の実験を行います.
class A
{
public:
	A(int& c): _b(2), _c(c)
	{
		_a = 1;
	}
protected:
	int _a;
	const int _b;
	int& _c;

};

class B
{
public:
	B(int& c):_objA(c)
	{
		printf("B constructor
"); } protected: A _objA; }; int _tmain(int argc, _TCHAR* argv[]) { int number = 3; B obj2(number); return 0; }

クラスBにはクラスAのオブジェクトがあり、クラスBのコンストラクタではコロンでメンバーを初期化します.objA.ではobjAはいつ初期化されたのですか?真実がある:
 
callstackの中からはっきり見えます.
1.Bの構造関数に入る.
2.Aのコンストラクタに入ります.
すなわち,コロンの後ろのコードは構造関数に入ると呼び出される.
それから左下のWatchからも見えますが、システムがコンストラクタカッコの最初の行のコードを呼び出す前に、a,_b, _cはもう割り当てられています.ご覧のようにaは初期化されていない値です(システム自体が1つ生成しました),bと_cはすべて私たちが初期化したものです.では、私は結論を出すことができます.
コンストラクション関数の後に続くコロンコードは、コンストラクション関数に入り、カッコ内の最初の行のコードの前に実行されます.
 
Bのコンストラクタで明示的に初期化しない場合_ObjA、何が起こるの?コードシミュレーションでわかるように、Aのデフォルトコンストラクタを呼び出して初期化します.objA.
 
はい、終わりました.一般的には,コンストラクション関数の後ろのコロンが初期化であり,括弧の中の等号は初期化ではなく変数生成後の付与である(永遠に2つのステップである).
 
添付:
本文の前に私は1つの言葉に言及します:constと引用は値を付与することができなくて、初期化するしかありません.この言葉に意見がある人もいるかもしれませんが、次のコードを見てください.このコードは正しいので、問題ありません.では、どのようにして割り当てられないのでしょうか.実はb=12はaの内容を変更しただけで(aとbの値はすべて12)、参照bを別の変数に指すのではありません.言い換えれば、bを参照して初期化が完了すると、初期化時の変数を永遠に指し、これ以上変更することはできません.ここで、「参照は付与できません」とは、参照自体に値を付与して指向を変更できないことを意味し、参照が指すメモリの内容を変更できないわけではありません.言葉には違う理解があるかもしれませんが、このようなことを知っていればいいのです.
int _tmain(int argc, _TCHAR* argv[])
{
	int a = 1;
	int& b = a;
	b = 12;

	return 0;
}