C ++クラスメンバの初期化にイニシャライザリストを使用する必要があるのはいつですか?
-
06-07-2019 - |
質問
私が持っているとしましょう
std::map< std::string, std::string > m_someMap
クラスAのプライベートメンバー変数として
2つの質問:(そして、私が尋ねている唯一の理由は、そのようなコードに出くわしたからです)
-
この行の目的は何ですか:
A::A() : m_someMap()
今、これは初期化であることを知っていますが、そのようにする必要がありますか? 混乱しています。
-
<=>のデフォルト値は何ですか。C#では、int、doubleなどは常にデフォルト0に初期化され、オブジェクトはnullになります(少なくともほとんどの場合) それでは、C ++のルールは何ですか??オブジェクトはdefualtによってnullに初期化され、プリミティブはガベージに初期化されますか? もちろん、インスタンス変数について取り上げています。
編集:
また、ほとんどの人がこれはスタイルの選択であり、必要ではないと指摘したので、次のことについてはどうですか:
A :: A():m_someMap()、m_someint(0)、m_somebool(false)
解決
m_somemap
- する必要はありません。
- 省略した場合に得られるもの:空の
std::map< std::string, std::string >
、つまり、要素を持たないマップの有効なインスタンス。
m_somebool
- 既知の値にしたい場合は、
true
またはfalse
に初期化する必要があります。ブール値は<!> quot;プレーンな古いデータ型<!> quot;また、コンストラクターの概念はありません。さらに、C ++言語は、明示的に初期化されないブール値のデフォルト値を指定しません。 - 省略した場合に得られるもの:値が指定されていないブール型メンバー。これを行わずに、後でその値を使用する必要があります。そのため、このタイプのすべての値を初期化することを強くお勧めします。
m_someint
- 既知の値にする場合は、整数値に初期化する必要があります。整数は<!> quot;プレーンな古いデータ型<!> quot;また、コンストラクターの概念はありません。さらに、C ++言語は、明示的に初期化されていない整数のデフォルト値を指定しません。
- 省略した場合に得られるもの:値が指定されていないintメンバー。これを行わずに、後でその値を使用する必要があります。そのため、このタイプのすべての値を初期化することを強くお勧めします。
他のヒント
実際に行う必要はありません。
デフォルトのコンストラクターは自動的にそれを行います。
しかし、時々明示的にすることで、一種のドキュメントとして機能します:
class X
{
std::map<string,string> data;
Y somePropertyOfdata;
X()
:data() // Technically not needed
,somePropertyOfdata(data) // But it documents that data is finished construction
{} // before it is used here.
};
C ++のルールは、PODデータを明示的に初期化しない限り未定義であり、他のクラスにはデフォルトコンストラクターが自動的に呼び出されることです(プログラマーが明示的に初期化しない場合でも)。
しかし、そう言っています。これを考慮してください:
template<typename T>
class Z
{
T data;
Z()
:data() // Technicall not need as default constructor will
// always be called for classes.
// But doing this will initialize POD data correctly
// if T is a basic POD type.
{}
};
ここでは、デフォルトで初期化されるデータを実行します。
技術的には、PODにはコンストラクターがないため、Tがintの場合、何かを行うことが期待できますか?明示的に初期化されたため、0またはPODタイプの同等に設定されます。
編集の場合:
class A
{
std::map<string,string> m_someMap;
int m_someint;
bool m_somebool;
public:
A::A()
: m_someMap() // Class will always be initialised (so optional)
, m_someint(0) // without this POD will be undefined
, m_somebool(false)// without this POD will be undefined
{}
};
他の人が指摘したように、それは必要ではありませんが、多かれ少なかれスタイルの問題です。 利点:明示的にデフォルトのコンストラクタを使用したいことを示し、コードをより冗長にします。欠点:複数のctorがある場合、それらすべての変更を維持するのは面倒な場合があり、時にはクラスメンバーを追加し、それらをctorsイニシャライザーリストに追加して一貫性のない外観にすることを忘れることがあります。
A::A() : m_someMap()
この場合、この行は不要です。ただし、一般的に 、クラスメンバーを初期化する唯一の適切な方法です。
次のようなコンストラクタがある場合:
X() : y(z) {
w = 42;
}
その後、X
コンストラクターが呼び出されると、次のことが起こります。
- 最初に、すべてのメンバーが初期化されます。
y
の場合、引数としてz
を受け取るコンストラクターを呼び出すことを明示的に指定します。w
の場合、何が起こるかは:
のタイプによって異なります。m_someMap
がPODタイプ(つまり、基本的にC互換タイプ:継承なし、コンストラクターまたはデストラクタなし、すべてのメンバーパブリック、およびすべてのメンバーもPODタイプ)の場合、 not が初期化されました。その初期値は、そのメモリアドレスで見つかったゴミです。: m_someMap()
が非POD型の場合、そのデフォルトのコンストラクターが呼び出されます(非POD型は、構築時に常に初期化されます)。 - 両方のメンバーが作成されたら、代入演算子を呼び出して42を<=>に割り当てます。
重要なことは、コンストラクターの本体に入る前にすべてのコンストラクターが 呼び出されることです。本体に入ると、すべてのメンバーはすでに初期化されています。 そのため、コンストラクター本体には2つの問題が考えられます。
- <=>がデフォルトのコンストラクターを持たないタイプの場合はどうなりますか?その後、これはコンパイルされません。次に、<=>のように、<=>の後に明示的に初期化する必要があります。
- デフォルトのコンストラクタと代入演算子の両方を両方呼び出すこのシーケンスが不必要に遅い場合はどうなりますか?おそらく、正しいコンストラクターを呼び出して開始する方がはるかに効率的です。
つまり、<=>は非POD型なので、厳密に言うと<=>を実行する必要はありません。とにかくデフォルトで構築されていたでしょう。しかし、PODタイプだった場合、またはデフォルトコンストラクター以外のコンストラクターを呼び出したい場合は、これを行う必要があります。
(2番目の質問に関して)何が起こっているのかを明確にするだけです
std::map< std::string, std::string > m_someMap
はm_someMapというスタック変数を作成し、デフォルトのコンストラクターが呼び出されます。すべてのオブジェクトのC ++のルールは、次のとおりです。
T varName;
Tは型、varNameはデフォルトで構築されます。
T* varName;
NULL(または0)に明示的に割り当てる必要があります-新しい標準ではnullptr。
デフォルト値の問題を明確にするには:
C ++には、一部のタイプの暗黙の参照による参照という概念がありません。何かがポインターとして明示的に宣言されていない限り、ヌル値を取ることはできません 。これは、コンストラクタパラメータが指定されていない場合に、 every クラスに初期値を構築するためのデフォルトコンストラクタがあることを意味します。デフォルトのコンストラクターが宣言されていない場合、コンパイラーがコンストラクターを生成します。また、クラスにクラス型のメンバーが含まれる場合、それらのメンバーはオブジェクト構築時に独自のデフォルトコンストラクターによって暗黙的に初期化されます。ただし、コロン構文を使用して別のコンストラクターを明示的に呼び出す場合は。 p>
たまたま、すべてのSTLコンテナタイプのデフォルトコンストラクタが空のコンテナを作成します。他のクラスには、デフォルトのコンストラクターが行うことについて他の規則がある場合があるため、このような状況で呼び出されることに注意してください。そのため、A::A() : m_someMap()
行は、実際にはコンパイラーに既に実行していることを実行するよう指示しているだけです。
C ++でオブジェクトを作成すると、コンストラクターは次のシーケンスを実行します。
- クラスツリー全体のすべての親仮想クラスのコンストラクターを呼び出します(任意の順序で)
- すべての直接継承された親クラスのコンストラクターを宣言順に呼び出します
- すべてのメンバー変数のコンストラクターを宣言順に呼び出します
これよりもいくつかの詳細があり、一部のコンパイラでは、この特定の順序からいくつかのことを強制することができますが、これは一般的な考え方です。これらのコンストラクター呼び出しごとに、コンストラクター引数を指定できます。その場合、C ++は指定どおりにコンストラクターを呼び出すか、そのままにしてC ++がデフォルトのコンストラクターを呼び出そうとします。デフォルトのコンストラクターは、単に引数を取らないコンストラクターです。
仮想親クラス、非仮想親クラス、またはメンバー変数のいずれかにデフォルトのコンストラクターがない場合、またはデフォルト以外のもので作成する必要がある場合は、コンストラクター呼び出しのリストにそれらを追加します。 C ++はデフォルトのコンストラクター呼び出しを想定しているため、リストにデフォルトのコンストラクターを置くことと完全に省略することにはまったく違いはありません(C ++は(この質問の範囲外の特別な状況ではない限り)を呼び出さずにオブジェクトを作成しませんある種のコンストラクタ)。クラスにデフォルトのコンストラクターがなく、コンストラクター呼び出しを提供しない場合、コンパイラーはエラーをスローします。
float
やint
などの組み込み型になると、デフォルトのコンストラクターは何もしません。そのため、変数はメモリーに残ったもののデフォルト値を持ちます。すべての組み込み型にはコピーコンストラクターもあるため、変数のコンストラクターへの唯一の引数として初期値を渡すことで初期化できます。