web-dev-qa-db-ja.com

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

プライベートメンバーptrnamepnamernamecrname、およびageを含むクラスがあるとします。自分で初期化しないとどうなりますか?以下に例を示します。

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

    public:
        Example() {}
};

そして、私は:

int main() {
    Example ex;
}

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

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

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

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

131
bodacydo

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

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

プリミティブ型(ポインター、整数など)の場合、それらはnot初期化されます-任意のジャンクが発生しました以前にそのメモリ位置に。

参照(例:std::string&)の場合、初期化しないのはillegalであり、コンパイラーは文句を言い、そのようなコードのコンパイルを拒否します。参照は常に初期化する必要があります。

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

    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
170
Tyler McHenry

まず、mem-initializer-listとは何かを説明します。 mem-initializer-listは、コンマ区切りのリストです mem-initializers、各 mem-initializer メンバー名の後に(が続き、expression-listが続き、)が続きます。 expression-listは、メンバーの構築方法です。たとえば、

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-initializer-listは、nameメンバーが 2つの入力反復子を受け取るstd::stringコンストラクター によって初期化されることを意味し、rnameメンバーはnameへの参照で初期化され、crnameメンバーはnameへのconst参照で初期化され、ageメンバーは値-4で初期化されます。

各コンストラクターには独自のmem-initializer-listがあり、メンバーは規定の順序(基本的にはクラス内でメンバーが宣言される順序)でのみ初期化できます。したがって、Exampleのメンバーは、ptrnamepnamernamecrname、およびageの順序でのみ初期化できます。

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

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

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

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

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

26
Daniel Trebbien

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

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番目の文字列クラスには初期化子がないため、作成時にデフォルトのコンストラクターが呼び出されます-おそらく自動的に空の文字列に設定されます。

10
user3259248

サンプルクラスがスタックでインスタンス化されている場合、初期化されていないスカラーメンバーの内容はランダムで未定義です。

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

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

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

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

もちろん、オブジェクトパラメータ(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
4
Wizard

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

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

1
janm

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

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

0
George