无法理解奇怪的程序行为 - 希望有人可以解释。
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
但这是陷阱!
答案 0 :(得分:10)
未指定不同翻译单元中全局变量的初始化顺序。可以首先构建main.cpp中的dummy
或两个全局int
。
在您的情况下,首先初始化了res1
和res2
,因此您通过调用尚未构造的对象上的成员函数来调用未定义的行为。
要解决此问题,请尝试以下操作:
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语句添加到Get
和Init
。
您所经历的内容也称为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 ++标准完全没有保证 - 我只是建议我亲眼看到的“订单可能很重要”。由于对这些内容没有特别要求,因此允许编译器供应商随时以任何方式更改它。