在main之前使用来自不同文件的变量

时间:2015-11-11 16:54:15

标签: c++ namespaces static-initialization

我很难理解为什么我的代码按照它的方式工作(或者不按照应有的方式工作)。

我尝试编写(在C ++中)一个接口,该接口允许在C中的标准模板库中使用unordered_map上运行的某些函数。但是,我也想写一个允许在C ++中使用它们的命名空间。

我问的问题不在于如何以不同的方式进行,而是为什么它的工作方式如此;

让我们说一段时间我只需要两个功能:添加元素并写入地图的大小。标题如下:

//project.h
#ifdef __cplusplus
extern "C" {
#endif

void add(int, int);

void give_size();

#ifdef __cplusplus
}
#endif

源代码:

//project.cc
#include <unordered_map>
#include <iostream>
#include "project.h"

using namespace std;

unordered_map<int, int> my_map;

void add(int arg, int val) {
    my_map.insert ({{arg, val}});
}

void give_size() {
    cout << my_map.size() << endl;
}

C ++的界面:

//cproject
namespace pro {
    #include "project.h"
}

和测试:

//test.cc
#include "cproject"
namespace {
    unsigned long test() {
        ::pro::add(1,2);
        ::pro::add(3,4);
        return 0;
    }
    unsigned long dummy = test();
}
int main() {
    ::pro::give_size();
    return 0;
}

而且,为了完整性,Makefile

g++ -Wall -std=c++11 -c -o project.o project.cc
g++ -Wall -std=c++11 -c -o test.o test.cc
g++ test.o project.o -o test

问题当然是运行test输出0而不是2 - 这意味着地图会在test之前的某处消失main

我认为它可能是某种static initialization order fiasco,但是我发现附加的解决方案非常有用,因为我没有明确地调用project.cc文件中的对象在test.cc

我很感激对此问题的任何帮助。

1 个答案:

答案 0 :(得分:2)

是的,这是命名不佳的静态初始化顺序惨败。命名不佳,因为C ++标准称之为“动态初始化”; “静态初始化”是不同的东西。

  

这意味着地图在测试的主要

之前的某处消失了

不完全。问题是你在它之前使用地图 ,为它添加值。现在碰巧,对于某些地图实现,零初始化状态(这是在任何动态初始化程序运行之前对所有全局变量执行的操作)与默认构造函数的作用相同。因此,test中的代码首先执行并尝试向地图添加内容,并且地图的插入函数工作正常,创建节点,设置节点的内部指针等。

然后运行映射的实际默认构造函数,将这些指针重置为null,泄漏并忘记您创建的所有节点。您之前的插入已撤消,地图再次为空。

您链接中提供的解决方案可行;你隐式地通过自由函数调用对象,即使你没有明确地这样做。没有真正的区别。您仍然使用一个函数替换project.cc中的全局my_map,该函数返回对函数级静态(或指针的引用,具体取决于您选择的确切解决方案)。唯一的区别是,您不是在test.cc中调用此函数,而是在addgive_size内调用此函数。

作为旁注,整个全球国家的事情通常都是可疑的。它不是线程安全的,它使得理解程序正在做什么变得更加困难。考虑不要这样做。