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



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

哑巴:

#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*/

标题.h:

#include "dummy.h"
extern Dummy dummy;

虚拟.cpp:

#include "dummy.h"
Dummy dummy;

主.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()函数中调用的第二个Get()返回-1?为什么分配的值从虚拟实例中消失并且没有调用解构函数。它是如何变成-1的?

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

新输出:

init
get
constructed
2
2
get
-1
deconstructed

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

g++ -c dummy.cpp 
g++ -c main.cpp 
g++ dummy.o main.o 
./a.out
constructed
init
get
2
2
get
2
deconstructed

但这是一个陷阱!

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

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

要修复它,请尝试以下操作:

标题.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;
}

主.cpp

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

如果它有助于理解,也向GetInit添加一些调试 cout 语句。您所经历的也称为静态初始化顺序惨败。

因此,发生的情况是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++标准根本没有提供任何保证——我只是建议从我个人看到的"顺序可能很重要"。由于对这些东西没有特别的要求,编译器供应商可以随时以他们喜欢的任何方式更改它。

最新更新