5

奇妙なプログラムの動作を理解できません - 誰かが説明してくれることを願っています。

ダミー.h:

#ifndef DUMMY_H
#define DUMMY_H

#include <iostream>

class Dummy
{
    int val;

public:
    int Init(int new_val)
    {
        return val = new_val;
    }

    int Get()
    {
        return val;
    }

    Dummy():
        val(-1)
    {
        std::cout << "constructed" << std::endl;
    }

    ~Dummy()
    {
        std::cout << "deconstructed" << std::endl;
    }
};

#endif /*DUMMY_H*/

header.h:

#include "dummy.h"

extern Dummy dummy;

ダミー.cpp:

#include "dummy.h"

Dummy dummy;

main.cpp:

#include <iostream>

#include "header.h"

int res1 = dummy.Init(2);
int res2 = dummy.Get();

int main()
{
    std::cout << res1 << std::endl;
    std::cout << res2 << std::endl;
    std::cout << dummy.Get() << std::endl;

    return 0;
}

コンパイル:g++ -Wall -Wextra main.cpp dummy.cpp

出力:

constructed
2
2
-1
deconstructed

main() 関数で呼び出された 2 番目の Get() が -1 を返すのはなぜですか? 割り当てられた値がダミー インスタンスから消え、デコンストラクターが呼び出されなかった理由。どのように -1 になるのですか?

upd: Init() と Get() にデバッグ情報を追加:

新しい出力:

init
get
constructed
2
2
get
-1
deconstructed

upd2: 面白い事実 - 1 つの実行可能ファイルでオブジェクト ファイルを別々にコンパイルおよびリンクすると、状況が変わりました。

g++ -c dummy.cpp 
g++ -c main.cpp 
g++ dummy.o main.o 

./a.out

constructed
init
get
2
2
get
2
deconstructed

しかし、それは罠です!

4

2 に答える 2

10

異なる翻訳単位でのグローバル変数の初期化の順序は規定されていません。main.cpp 内のどちらdummyか、または 2 つの globalintを最初に構築できます。

あなたの場合、res1最初res2に初期化されたので、まだ構築されていないオブジェクトでメンバー関数を呼び出して、未定義の動作を呼び出しました。

それを修正するには、これを試してください:

header.h:

#include "dummy.h"

Dummy& getDummy();

ダミー.cpp:

#include "dummy.h"

Dummy& getDummy()
{
    static Dummy dummy; // gets initialized at first call
                        // and persists for the duration of the program
    return dummy;
}

main.cpp

int res1 = getDummy().Init(2);
int res2 = getDummy().Get();

理解に役立つ場合は、いくつかの debug cout ステートメントをGetおよびInitにも追加してください。あなたが経験したことは、静的初期化順序の失敗としても知られています。

于 2013-09-18T11:32:22.643 に答える
4

したがって、何が起こるかというと、初期化される前にint res1 = dummy.Init(2);andint res2 = dummy.Get();が実行dummyされます。次に、beforemainが入力され、dummy構築され、値が に-1格納されvalます。

この特定のケースでは、このバージョンのコンパイラでは、オブジェクト ファイルを-g++ -o myprog dummy.o main.oの代わりに再配置することで、これを変更できる可能性があります。g++ -o myprog main.o dummy.o再度変更します。C++ 標準はまったく保証していません。私が個人的に見たものから、「順序が重要である可能性がある」ことを示唆しているだけです。また、これらについては特に要件がないため、コンパイラ ベンダーはいつでも好きなように変更することができます。

于 2013-09-18T11:41:35.040 に答える