質問
単純なC ++クラスで奇妙な動作に遭遇しました。
classA.h
class A
{
public:
A();
~A();
static const std::string CONST_STR;
};
classA.cpp
#include "classA.h"
#include <cassert>
const std::string A::CONST_STR("some text");
A::A()
{
assert(!CONST_STR.empty()); //OK
}
A::~A()
{
assert(!CONST_STR.empty()); //fails
}
main.cpp
#include <memory>
#include <classA.h>
std::auto_ptr<A> g_aStuff;
int main()
{
//do something ...
g_aStuff = std::auto_ptr<A>(new A());
//do something ...
return 0;
}
アクセス違反などを期待しますが、静的なconst文字列の内容が変更されるとは決して思いません。ここで誰かがそのコードで何が起こるかについて良い説明を持っていますか?
ありがとう、 ノーバート
解決
アクセス違反または 似たようなものですが、私は期待していません 静的constのコンテンツ 文字列が変更される可能性があります。
未定義の動作:未定義です。 CONST_STRが破棄された場合、それにアクセスした場合にハードウェア例外が保証されません。クラッシュする可能性がありますが、そのアドレスは空の文字列のように見えるデータを含むことになります。デストラクタはポインタなどをクリアします。
この場合、Aインスタンスは、main()で割り当てられるグローバルスマートポインターにも格納されると言います。そのため、CONST_STRはAコンストラクターでアクセスされたときに構築されましたが、スマートポインターが破棄される前に破棄される可能性が非常に高いです。確実に言うにはプログラム全体が必要です。
[編集:これで完了です。 CONST_STRとg_aStuffは異なるコンパイル単位で定義されているため、それらの相対的な構築順序は標準では定義されていません。 CONST_STRが最初に破壊されると推測しています。]
他のヒント
編集:明らかに、欠落している A ::
は、元のコード投稿のタイプミスでした。
元の回答:
持っているつもりですか
const std::string A::CONST_STR("some text");
CONST_STRがクラス A
の一部になるように
それ以外の場合は、個別に宣言し、 A
の静的メンバーを初期化しない 。
2つの異なるコンパイル単位で2つの静的変数を作成しています。どの順序で初期化されるかを知る方法はありません。ただし、デストラクタは常に逆の順序で呼び出されます。
あなたの場合、次のシナリオが行われたようです:
g_aStuff constructor
CONST_STR constructor
main funciton initializes g_aStuff
CONST_str destructor
g_aStuff descrutor
この時点では、CONST_STR.empty()の結果は未定義です。アサートをトリガーする可能性があります。
classA.cppで定義された
const std::string CONST_STR("some text");
はAのメンバーではありません。その定義は次のようになります。
const std::string A::CONST_STR("some text");
標準では、異なる翻訳単位でのグローバル/静的オブジェクトの初期化の順序は指定されていません。ただし、そのような各オブジェクトは、その翻訳単位の関数が実行される前に初期化されることを保証します。
あなたの場合、 CONST_STR
は g_aStuff
の後に初期化され、破壊の順序は構築の順序と逆なので、その前に破壊されます。したがって、 A
のデストラクタから CONST_STR
にアクセスすると、未定義の動作が呼び出されます。アクセス違反が発生する場合と発生しない場合があります。
CONST_STR
は、同じ変換単位にあるため、 A
のコンストラクターが実行される前に初期化されます。
Aのグローバルインスタンス(またはタイプAの静的クラスメンバー)がある場合に発生する可能性があります。グローバルと静的の初期化の順序は定義されていないため(相互変換単位)、定義できます。
完全なコードを見ると、コンパイル単位(classA.cppおよびmain.cpp)全体の破棄の順序に依存しています。 mainでg_aStuffをローカルとして作成する場合、アサートは成功するはずです。