2

我一直在尝试调试这个几个小时,但没有运气。我知道你们会在几分钟内解决问题,所以情况如下:

我有大约 400 个 .cpp/.h 文件,称为 ProblemX.cpp/ProblemX.h(其中 X 是从 1 到 400 的数字)。每个文件都包含数学相关问题的解决方案。我想让问题在编译时将它们自己注册到具有唯一键的全局映射中(只需一个 int 就可以了),并将值作为指向启动数学问题解决方案的函数的指针。

全局映射在名为 Problem.h/Problem.cpp 的文件中创建和处理。但是,当第一个问题尝试在地图中自我注册时,我收到“访问冲突读取位置 0x00000004”。代码如下:

在 ProblemX.h 文件中(problem1 启动了此问题的解决方案):

#ifndef PROBLEM1_H
#define PROBLEM1_H

#include "Problems.h"
#include <string>

std::string problem1();
static int rc1 = registerProblem(1, problem1);

#endif

在 Problems.h 文件中(problemFinder 是使用全局映射调用相应函数指针的函数):

#ifndef PROBLEMS_H
#define PROBLEMS_H

#include <string>

int registerProblem(int problemNum, std::string (*problemFunc)(void));
std::string problemFinder(int problemNum);

#endif

在 Problems.cpp 中:

#include "Problems.h"
#include <iostream>
#include <map>

using namespace std;

map<int,std::string (*)(void)> problems_map;

int registerProblem(int problemNum, string (*problemFunc)(void)) {
    int rc = 0;
    problems_map[problemNum] = problemFunc;
    return rc;
}


string problemFinder(int problemNum) {
    string retStr = "";
    retStr = problems_map[problemNum]();
    return retStr;
}

访问冲突发生在“problems_map[problemNum] = problemFunc;”的位置。

谢谢!

4

3 回答 3

4

正如神秘命名的 user93353 所回答的那样,problems_map不能保证在其他文件中的全局变量之前构造全局变量。

为了避免静态初始化顺序失败,请创建problems_map一个本地静态,它将在首次使用时被初始化:

map<int,std::string (*)(void)>&
get_problems_map()
{
  static map<int,std::string (*)(void)> problems_map;
  return problems_map;
}

然后像这样使用它:

get_problems_map()[problemNum] = problemFunc;

这样可以确保在需要时立即创建地图,而不仅仅是在Problems.cppget 的全局构造函数运行时,这可能是(在你的情况下肯定是)在全局rc1变量初始化之后。

于 2012-11-13T00:19:36.797 回答
3

小心使用这样的静力学。 它们的定义顺序可能会有所不同。变量可能存在,但不一定是构造的。这实际上是昨天咬我的屁股,所以它在我的记忆中很新鲜。

Problems.cpp中,事物定义的顺序是:

static int rc1 = registerProblem(1, problem1);
map<int,std::string (*)(void)> problems_map;

这意味着rc1在之前初始化problems_map

因此,您的问题出现是因为registerProblem被调用初始化rc1并且它使用problems_map尚未构建的。

我一直认为编译器会解决这个问题。但是当您考虑它时,一般情况很难考虑(特别是如果您最终会相互依赖)。所以我想唯一明智的做法是期望程序员以正确的顺序放置他们的静态定义,就像他们应该对任何其他代码语句做的那样。

于 2012-11-12T23:59:26.057 回答
1

检查 C++ FAQ on Constructors。特别是检查问题 10.14 到 10.18 - “静态初始化顺序惨败”。

什么是“静态初始化订单惨败”?

一种使程序崩溃的微妙方法。

静态初始化顺序惨败是 C++ 的一个非常微妙且经常被误解的方面。不幸的是,它很难检测到——错误通常发生在 main() 开始之前。

简而言之,假设您有两个静态对象 x 和 y,它们存在于单独的源文件中,例如 x.cpp 和 y.cpp。进一步假设 y 对象的初始化(通常是 y 对象的构造函数)调用 x 对象上的某个方法。

就是这样。就是这么简单。

于 2012-11-13T00:04:42.727 回答