15

C++ 標準は、初期化されていない POD メンバーが新しい配置後に以前の値を保持することを保証しますか?

より正確には、次のアサートは C++11 に従って常に満たされるのでしょうか?

#include <cstdlib>
#include <cassert>

struct Foo {
    int alpha; // NOTE: Uninitialized
    int beta = 0;
};

int main()
{
    void* p = std::malloc(sizeof (Foo));
    int i = some_random_integer();
    static_cast<Foo*>(p)->alpha = i;
    new (p) Foo;
    assert(static_cast<Foo*>(p)->alpha == i);
}

答えは C++03 でも同じですか?

4

2 に答える 2

20

C++ 標準は、初期化されていない POD メンバーが新しい配置後に以前の値を保持することを保証しますか?

次のアサートは、C++11 に従って常に満たされますか?

いいえ。

初期化されていないデータ メンバーの値は不定です。これは、基礎となるメモリが放置されているということとまったく同じではありません。

[C++11: 5.3.4/15]:type のオブジェクトを作成するnewTは、次のようにそのオブジェクトを初期化します。

  • new-initializerを省略した場合、オブジェクトはデフォルトで初期化されます(8.5)。初期化が実行されない場合、オブジェクトの値は不定です。
  • それ以外の場合、new-initializerは直接初期化の 8.5 の初期化規則に従って解釈されます。

[C++11: 8.5/6]:タイプのオブジェクトをデフォルトで初期化するとは、次のことをT意味します。

  • T(おそらくcv 修飾された)クラス型(第 9 節) である場合、のデフォルト コンストラクタTが呼び出されます(アクセス可能なデフォルト コンストラクタがない場合、初期化の形式は正しくありTません)。
  • が配列型の場合T、各要素はデフォルトで初期化されます。
  • それ以外の場合、初期化は実行されません。

[C++11: 12.1/6]:デフォルト設定され、削除済みとして定義されていないデフォルト コンストラクターは、そのクラス型 (1.8) のオブジェクトを作成するためにODR を使用(3.2) する場合、または最初の宣言後に明示的にデフォルト設定する場合に暗黙的に定義されます。暗黙的に定義されたデフォルト コンストラクターは、ctor -initializer (12.6.2) と空の Compound -statementを使用せずに、そのクラスのユーザー作成のデフォルト コンストラクターによって実行されるクラスの初期化のセットを実行します。

[C++11: 12.6.2/8]:非委任コンストラクターで、特定の非静的データ メンバーまたは基底クラスがmem-initializer-idによって指定されていない場合 (コンストラクターにctor-initializerがないためにmem-initializer-listがない場合を含む)エンティティが抽象クラス (10.4) の仮想基底クラスではない場合、

  • エンティティが、 brace-or-equal-initializerを持つ非静的データ メンバーである場合、エンティティは8.5 で指定されているように初期化されます。
  • それ以外の場合、エンティティがバリアント メンバー (9.5) の場合、初期化は実行されません。
  • それ以外の場合、エンティティはデフォルトで初期化されます(8.5)。

( NB.の最初のオプション12.6.2/8は、メンバーのbeta扱い方です)

[C++11: 8.5/6]:タイプのオブジェクトをデフォルトで初期化するとは、次のことをT意味します。

  • T(おそらくcv 修飾された) クラス型 (第 9 節) である場合、 のデフォルト コンストラクタTが呼び出されます (Tアクセス可能なデフォルト コンストラクタがない場合、初期化の形式は正しくありません)。
  • が配列型の場合T、各要素はデフォルトで初期化されます。
  • それ以外の場合、初期化は実行されません。

[C++11: 8.5/11]:オブジェクトに初期化子が指定されていない場合、オブジェクトはデフォルトで初期化されます。初期化が実行されない場合、自動または動的ストレージ期間を持つオブジェクトの値は不確定です。

コンパイラは、割り当て中に基になるメモリをゼロにする (または別の方法で変更する) ことを選択できます。たとえば、デバッグ モードの Visual Studio は、デバッグ0xDEADBEEFを支援するためにメモリなどに認識可能な値を書き込むことが知られています。この場合、0xCDCDCDCDどちらが「クリーン メモリ」を意味するのに使用されているかがわかります (参照)。

この場合、そうなりますか知らない。私たちは知ることができないと思います。

私たち知っていることは、C++ がそれを禁止していないということであり、それがこの回答の結論につながると信じています。:)


答えは C++03 でも同じですか?

はい、ただしロジックが少し異なります。

[C++03: 5.3.4/15]:type のオブジェクトを作成するnewTは、次のようにそのオブジェクトを初期化します。

  • new-initializerが省略された場合:
    • T(おそらくcv 修飾された)非 POD クラス型(またはその配列) である場合、オブジェクトはデフォルトで初期化されます (8.5)。が const 修飾された型である場合T、基になるクラス型には、ユーザーが宣言した既定のコンストラクターが必要です。
    • それ以外の場合、作成されたオブジェクトの値は不定です。がconst 修飾された型、または const 修飾された型のメンバーを (直接的または間接的に) 含む (Tおそらくcv 修飾された) POD クラス型 (またはその配列) である場合、プログラムは不正な形式です。
  • new-initializerの形式が の場合()、項目は値で初期化されます (8.5)。
  • new-initializerがフォーム(expression-list)Tあり、クラス型である場合、適切なコンストラクターが呼び出され、引数 (8.5) として使用さexpression-listれます。
  • new-initializerの形式が算術(expression-list)T列挙、ポインター、またはメンバーへのポインター型であり、式をexpression-list1 つだけ含む場合、オブジェクトは式 (8.5) の (場合によっては変換された) 値に初期化されます。
  • それ以外の場合、new-expressionは不適切な形式です。

以上が、初期化のルールに関する私の厳密な解釈でした。

operator new実際に言えば、配置構文の定義との潜在的な競合を見て、おそらく正しいと思います。

[C++11: 18.6.1/3]: 備考:意図的に他のアクションを実行しません。

次の例は、配置newが「既知のアドレスでオブジェクトを構築するのに役立つ可能性がある」ことを説明しています。

ただし、既存の値を変更せずに既知のアドレスでオブジェクトを構築する一般的な使用法については実際には言及していませんが、「他のアクションを実行しない」というフレーズは、意図が「不確定な値」であることを示唆しています。以前に記憶にあったものは何でも。

または、オペレーター自体がアクションを実行することを単純に禁止し、アロケーターを解放することもできます。標準が作成しようとしている重要なポイントは、新しいメモリが割り当てられていないことです

とにかく、このデータにアクセスすると、未定義の動作が発生します。

[C++11: 4.1/1]:非関数型、非配列型の glvalue (3.10) は、Tprvalue に変換できます。Tが不完全な型の場合、この変換を必要とするプログラムは形式が正しくありません。glvalue が参照するオブジェクトが型のオブジェクトでTはなく、 から派生した型のオブジェクトでない場合T、またはオブジェクトが初期化されていない場合、この変換を必要とするプログラムは未定義の動作をします。Tが非クラス型の場合、prvalue の型は の cv 非修飾バージョンですT。それ以外の場合、prvalue の型は ですT

したがって、それは問題ではありません。とにかく、元の値を順守して観察することはできませんでした。

于 2013-02-02T08:05:48.270 に答える
0

C++ 11 12.6.2/8「ベースとメンバーの初期化」は次のように述べています。

非委任コンストラクターで、特定の非静的データ メンバーまたは基底クラスが mem-initializer-id によって指定されていない場合 (コンストラクターに ctor-initializer がないために mem-initializer-list がない場合を含む)エンティティが抽象クラス (10.4) の仮想基底クラスではない場合、

  • エンティティがブレースまたはイコール初期化子を持つ非静的データ メンバーである場合、エンティティは 8.5 で指定されているように初期化されます。
  • それ以外の場合、エンティティがバリアント メンバー (9.5) の場合、初期化は実行されません。
  • それ以外の場合、エンティティはデフォルトで初期化されます(8.5)。

のデフォルトの初期化intは何もしません (8.5/6 "Initializers"):

タイプ T のオブジェクトをデフォルトで初期化するとは、次のことを意味します。

  • T が (おそらく cv 修飾された) クラス型 (第 9 節) である場合、T の既定のコンストラクターが呼び出されます (T にアクセス可能な既定のコンストラクターがない場合、初期化の形式は正しくありません)。
  • T が配列型の場合、各要素はデフォルトで初期化されます。
  • それ以外の場合、初期化は実行されません

したがって、メンバーは放っておくalpha必要があります。

于 2013-02-02T07:58:43.733 に答える