在main()消失之前初始化的值

时间:2013-09-18 11:27:52

标签: c++

无法理解奇怪的程序行为 - 希望有人可以解释。

dummy.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;

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;
}

编译:{{1​​}}

输出:

g++ -Wall -Wextra main.cpp dummy.cpp

为什么在main()函数中调用的第二个Get()返回-1?为什么指定的值从虚拟实例中消失,并且没有调用解构函数。怎么变成-1?

upd:将调试信息添加到Init()和Get():

新产品:

constructed
2
2
-1
deconstructed

upd2:有趣的事实 - 在一个可执行文件中单独编译和链接目标文件改变了这种情况:

init
get
constructed
2
2
get
-1
deconstructed

但这是陷阱!

2 个答案:

答案 0 :(得分:10)

未指定不同翻译单元中全局变量的初始化顺序。可以首先构建main.cpp中的dummy或两个全局int

在您的情况下,首先初始化了res1res2,因此您通过调用尚未构造的对象上的成员函数来调用未定义的行为。

要解决此问题,请尝试以下操作:

header.h:

#include "dummy.h"

Dummy& getDummy();

dummy.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();

如果它有助于理解,请将一些调试cout语句添加到GetInit。 您所经历的内容也称为static initialization order fiasco

答案 1 :(得分:4)

因此,在int res1 = dummy.Init(2);初始化之前运行int res2 = dummy.Get();dummy会发生什么。然后,在输入main之前,构建dummy并将值-1存储在val中。

对于此版本的编译器,您可以通过将目标文件重新排列为g++ -o myprog dummy.o main.o而不是g++ -o myprog main.o dummy.o来更改此特定情况,但是这并不能保证解决问题,并且在未来版本的编译器/链接器中,结果可能会再次发生变化。 C ++标准完全没有保证 - 我只是建议我亲眼看到的“订单可能很重要”。由于对这些内容没有特别要求,因此允许编译器供应商随时以任何方式更改它。