C ++クラスメンバーを明示的に初期化しない場合、どのように初期化されますか?


158

私はプライベートmemebersを持つクラスを持っていると仮定しptrnamepnamernamecrnameage。自分で初期化しないとどうなりますか?次に例を示します。

class Example {
    private:
        int *ptr;
        string name;
        string *pname;
        string &rname;
        const string &crname;
        int age;

    public:
        Example() {}
};

そして私はします:

int main() {
    Example ex;
}

メンバーはどのようにexで初期化されますか?ポインタはどうなりますか?やるstringintデフォルトコンストラクタで0-intializedますstring()int()?参照メンバーはどうですか?また、const参照についてはどうですか?

他に何について知っておくべきですか?

これらのケースをカバーするチュートリアルを知っている人はいますか?たぶんいくつかの本に?私は大学の図書館で多くのC ++の本にアクセスできます。

私はそれを学びたいので、より良い(バグのない)プログラムを書くことができます。フィードバックは役に立ちます!


3
書籍の推奨事項については、stackoverflow.com / questions / 388242 /…を
Mike Seymour

マイク、そう、私はそれを説明する本の章を意味しています。本全体ではありません!:)
bodacydo

ただし、プログラミングする予定の言語についての本全体を読むことをお勧めします。そして、あなたがすでにそれを読んでいて、それがこれを説明していないなら、それはとても良い本ではありませんでした。
Tyler McHenry

2
Scott Meyers(人気のある元プロC ++アドバイスの第一人者)は、Effective C ++で「ルールは複雑です-覚えるだけの価値があるほど複雑なので、...すべてのコンストラクターがオブジェクトのすべてを初期化することを確認してください」と述べています。だから彼の意見では、「バグのない」コードを書く(試みる)の最も簡単な方法は、ルールを覚えようとすること(実際にはルールを本にレイアウトしないこと)ではなく、すべてを明示的に初期化することです。ただし、独自のコードでこのアプローチをとっても、そうでない人が作成したプロジェクトで作業する可能性があるので、ルールは依然として価値があるかもしれません。
カイルストランド

2
@TylerMcHenry C ++について「良い」と思う本は何ですか。私はC ++に関する本をいくつか読んだことがありますが、どれもこれを完全に説明していません。以前のコメントで述べたように、Scott Meyers は、Effective C ++で完全なルールを提供することを明示的に拒否しています。Meyers ' Effective Modern C ++、DewhurstのC ++ Common Knowledge、およびStroustrupのA Tour of C ++も読んだ。私の記憶では、誰も完全な規則を説明していませんでした。当然、私は標準を読むことができたかもしれませんが、それを「良い本」とはほとんど思いません!:Dそして、StroustrupがおそらくC ++プログラミング言語で説明していると思います。
カイルストランド

回答:


207

明示的な初期化の代わりに、クラスのメンバーの初期化は、関数のローカル変数の初期化と同じように機能します。

以下のためのオブジェクト、それらのデフォルトコンストラクタが呼び出されます。たとえば、std::string、の場合、デフォルトのコンストラクタはそれを空の文字列に設定します。オブジェクトのクラスにデフォルトのコンストラクタがない場合、明示的に初期化しないと、コンパイルエラーになります。

以下のためにプリミティブ型(ポインタ、int型など)、彼らはありません初期化-彼らは任意のジャンクは、以前にそのメモリ位置にあることを起こっ何が含まれています。

言及(例えばstd::string&)、それは違法、それらを初期化しないように、そしてあなたのコンパイラが文句を言うと、そのようなコードをコンパイルすることを拒否します。参照は常に初期化する必要があります。

したがって、特定のケースで、明示的に初期化されていない場合:

    int *ptr;  // Contains junk
    string name;  // Empty string
    string *pname;  // Contains junk
    string &rname;  // Compile error
    const string &crname;  // Compile error
    int age;  // Contains junk

4
+1。厳密な標準定義により、プリミティブ型のインスタンスは、他のさまざまなもの(ストレージの任意の領域)とすべてオブジェクトと見なされることに注意してください。
stinky472 2010年

7
「オブジェクトのクラスにデフォルトのコンストラクタがない場合、明示的に初期化しないとコンパイルエラーになります」これは誤りです。クラスにデフォルトのコンストラクタがない場合は、デフォルトのデフォルトのコンストラクタが空になります。
ウィザード、

19
@wiz彼は文字通り「オブジェクトにデフォルトのコンストラクターがない場合」も生成されたものとは異なり、クラスがデフォルト以外のコンストラクターを明示的に定義している場合(デフォルトのctorは生成されません)を意味すると思います。もし私たちがあまりにも多くの知識を身につけたら、私たちはおそらく助けよりも混乱するでしょうし、タイラーは以前の私への彼の返答でこれについて良い点を述べています。
stinky472 2010年

8
@ wiz-loz私はそれfooがコンストラクターを持っていると言うでしょう、それは単に暗黙的です。しかし、それは実際には意味論の議論です。
タイラーマクヘンリー

4
「デフォルトのコンストラクタ」を、引数なしで呼び出せるコンストラクタとして解釈します。これは、自分で定義するか、コンパイラによって暗黙的に生成されるものです。したがって、それがないということは、自分で定義したり生成したりしないことを意味します。それとも私はそれを見る方法です。
5ラウンド

28

まず、mem-initializer-listとは何かについて説明します。MEM-初期リストは、コンマで区切ったリストであり、MEM-初期それぞれS、MEM-イニシャライザが続くメンバ名(に続く、式リストに続きます、)表現リストは、メンバーが構築される方法です。たとえば、

static const char s_str[] = "bodacydo";
class Example
{
private:
    int *ptr;
    string name;
    string *pname;
    string &rname;
    const string &crname;
    int age;

public:
    Example()
        : name(s_str, s_str + 8), rname(name), crname(name), age(-4)
    {
    }
};

ユーザー指定の引数なしのコンストラクターのmem-initializer-listname(s_str, s_str + 8), rname(name), crname(name), age(-4)です。このMEM-初期リストことを意味name部材によって初期化される二つの入力イテレータを取るコンストラクタは、部材を用いて初期化されますstd::stringrnamenamecrname部材へのconst参照で初期化されname、そしてage部材が値で初期化されます-4

各コンストラクターには独自のmem-initializer-listがあり、メンバーは所定の順序(基本的には、メンバーがクラスで宣言される順序)でのみ初期化できます。このように、のメンバーExample缶のみを順番に初期化される:ptrnamepnamernamecrname、とage

メンバーのmem-initializerを指定しない場合、C ++標準は次のように述べています。

エンティティがクラスタイプ...の非静的データメンバーである場合、エンティティはデフォルトで初期化されます(8.5)。...それ以外の場合、エンティティは初期化されません。

ここでnameは、はクラス型の非静的データメンバーであるため、初期化子name mem-initializer-listで初期化子ます。の他のすべてのメンバーにExampleはクラス型がないため、初期化されません。

標準で初期化されていないと記載されている場合、これはそれらが任意の値を持つことができることを意味します。したがって、上記のコードはを初期化しなかったため、pname何でもかまいません。

参照を常に初期化する必要があるというルールなど、他のルールに従う必要があることに注意してください。参照を初期化しないのはコンパイラエラーです。


これは、内部をあまり表示せずに宣言.h)と定義()を厳密に分離したい場合に、メンバーを初期化するための最良の方法です.cpp
Matthieu

12

宣言した時点でデータメンバーを初期化することもできます。

class another_example{
public:
    another_example();
    ~another_example();
private:
    int m_iInteger=10;
    double m_dDouble=10.765;
};

私はこのフォームをほとんど独占的に使用していますが、「悪いフォーム」と考える人もいますが、たぶん最近導入されたためかと思います。C++ 11だと思います。私にとってはもっと論理的です。

新しいルールのもう1つの便利な側面は、それ自体がクラスであるデータメンバーを初期化する方法です。たとえば、それCDynamicStringが文字列処理をカプセル化するクラスであるとします。これには、初期値を指定できるコンストラクタがありますCDynamicString(wchat_t* pstrInitialString)。このクラスを別のクラス内のデータメンバーとして使用することをお勧めします。たとえば、この場合は住所を格納するWindowsレジストリ値をカプセル化するクラスです。これが書き込むレジストリキー名を「ハードコード」するには、中かっこを使用します。

class Registry_Entry{
public:
    Registry_Entry();
    ~Registry_Entry();
    Commit();//Writes data to registry.
    Retrieve();//Reads data from registry;
private:
    CDynamicString m_cKeyName{L"Postal Address"};
    CDynamicString m_cAddress;
};

実際の住所を保持する2番目の文字列クラスには初期化子がないため、デフォルトのコンストラクターが作成時に呼び出されることに注意してください-おそらく自動的に空白文字列に設定します。


9

例のクラスがスタックでインスタンス化されている場合、初期化されていないスカラーメンバーの内容はランダムであり、定義されていません。

グローバルインスタンスの場合、初期化されていないスカラーメンバーはゼロになります。

それ自体がクラスのインスタンスであるメンバーの場合、デフォルトのコンストラクターが呼び出されるため、文字列オブジェクトが初期化されます。

  • int *ptr; //初期化されていないポインタ(またはグローバルの場合はゼロ)
  • string name; //コンストラクタが呼び出され、空の文字列で初期化されました
  • string *pname; //初期化されていないポインタ(またはグローバルの場合はゼロ)
  • string &rname; //初期化に失敗した場合のコンパイルエラー
  • const string &crname; //初期化に失敗した場合のコンパイルエラー
  • int age; //初期化されていないランダムなスカラー値(またはグローバルの場合はゼロ)

実験を行っstring nameたところ、スタック上のクラスを初期化した後、何もないようです。あなたの答えは本当に確信していますか?
bodacydo

1
文字列には、デフォルトで空の文字列を提供するコンストラクターがあります-私は私の答えを明確にします
Paul Dixon

@bodacydo:Paulは正解ですが、この振る舞いを気にしても、明示的に言っても害はありません。イニシャライザリストに入れます。
スティーブン

明確にして説明してくれてありがとう!
bodacydo

2
ランダムではありません!ランダムという言葉は大きすぎます!スカラーメンバーがランダムである場合、他の乱数ジェネレーターは必要ありません。メモリ内の削除を取り消すファイルのように、データの「残り」を分析するプログラムを想像してみてください。データはランダムではありません。それも未定義ではありません!通常、マシンの機能がわからないため、定義することは通常困難です。元に
戻した

5

初期化されていない非静的メンバーにはランダムなデータが含まれます。実際には、割り当てられたメモリ位置の値のみが含まれます。

もちろん、オブジェクトパラメータ(などstring)では、オブジェクトのコンストラクタがデフォルトの初期化を行うことができます。

あなたの例では:

int *ptr; // will point to a random memory location
string name; // empty string (due to string's default costructor)
string *pname; // will point to a random memory location
string &rname; // it would't compile
const string &crname; // it would't compile
int age; // random value

2

コンストラクターを持つメンバーには、初期化のために呼び出されるデフォルトのコンストラクターがあります。

他のタイプのコンテンツに依存することはできません。


0

スタック上にある場合、独自のコンストラクターを持たない初期化されていないメンバーの内容はランダムで未定義になります。それがグローバルであるとしても、それらがゼロにされることに依存することは悪い考えです。スタック上にあるかどうかに関係なく、メンバーに独自のコンストラクターがある場合は、コンストラクターが呼び出されて初期化されます。

したがって、string * pnameがある場合、ポインタにはランダムなジャンクが含まれます。ただし、文字列名の場合、文字列のデフォルトのコンストラクターが呼び出され、空の文字列が提供されます。参照型の変数についてはわかりませんが、ランダムなメモリのチャンクへの参照になる可能性があります。


0

クラスの構築方法によって異なります

この質問に答えると、C ++言語標準の巨大なswitch caseステートメントを理解することができます。

難しいことの簡単な例として:

main.cpp

#include <cassert>

int main() {
    struct C { int i; };

    // This syntax is called "default initialization"
    C a;
    // i undefined

    // This syntax is called "value initialization"
    C b{};
    assert(b.i == 0);
}

デフォルトの初期化では、https//en.cppreference.com/w/cpp/language/default_initializationから開始します「デフォルトの初期化の効果」の部分に移動して、caseステートメントを開始します。

  • 「Tが非PODの場合」:いいえ(PODの定義自体が巨大なswitchステートメントです)
  • 「Tが配列型の場合」:いいえ
  • 「それ以外の場合は、何も行われません」:したがって、未定義の値が残ります

次に、誰かが値の初期化を決定した場合、https://en.cppreference.com/w/cpp/language/value_initializationに移動し、「値の初期化の効果は次のとおりです」とcaseステートメントを開始します。

  • 「Tが、デフォルトコンストラクターがないか、ユーザーが指定または削除したデフォルトコンストラクターがあるクラス型の場合」:ケースではありません。これらの用語をグーグルで20分費やします。
    • 暗黙的に定義されたデフォルトコンストラクターがある(特に他のコンストラクターが定義されていないため)
    • ユーザー提供ではない(暗黙的に定義されている)
    • 削除されません(= delete
  • 「Tが、ユーザーが提供も削除もしないデフォルトのコンストラクターを持つクラス型である場合」:はい
    • 「オブジェクトはゼロで初期化され、重要なデフォルトコンストラクターがある場合はデフォルトで初期化されます」:重要なコンストラクターはなく、ゼロで初期化されます。"zero-initialize"の定義は少なくともシンプルで、期待どおりに動作します。https//en.cppreference.com/w/cpp/language/zero_initialization

これが、「暗黙の」ゼロ初期化に決して依存しないことを強くお勧めする理由です。強力なパフォーマンス上の理由がない限り、コンストラクターを定義した場合はコンストラクターで、または集約初期化を使用して、すべてを明示的に初期化します。そうしないと、将来の開発者にとって非常に危険なことになります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.