2

私はこれを何時間もデバッグしようとしてきましたが、うまくいきませんでした。皆さんなら数分以内に問題を解決できると思いますので、状況は次のとおりです。

ProblemX.cpp/ProblemX.h (X は 1 から 400 の数字) という .cpp/.h ファイルが 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.cppされた後である可能性がある(そしてあなたの場合は間違いなくそうである)からのグローバルコンストラクターが実行されるときだけでなく、必要に応じてすぐにマップが作成されます。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 を確認してください。特に、質問 10.14 から 10.18 - 「静的初期化順序の大失敗」を確認してください。

「静的初期化順序の大失敗」とは何ですか?

プログラムをクラッシュさせる微妙な方法。

静的な初期化順序の大失敗は、C++ の非常に微妙でよく誤解されている側面です。残念ながら、これを検出するのは非常に困難です。多くの場合、main() が始まる前にエラーが発生します。

簡単に言うと、x.cpp と y.cpp などの別々のソース ファイルに存在する 2 つの静的オブジェクト x と y があるとします。さらに、y オブジェクト (通常は y オブジェクトのコンストラクター) の初期化で x オブジェクトのメソッドが呼び出されるとします。

それでおしまい。それはとても簡単です。

于 2012-11-13T00:04:42.727 に答える