6

次のコードは、常に真の分岐に従っているようです。

#include <map>
#include <iostream>

class TestClass {
  // implementation 
}

int main() {
  std::map<int, TestClass*> TestMap;
  if (TestMap[203] == nullptr) {
    std::cout << "true";
  } else {
    std::cout << "false";
  }
  return 0;
}

初期化されていないポインタが を指すように定義された動作nullptrですか、それともコンパイラのアーティファクトですか?

そうでない場合、次のコードの移植性を確保するにはどうすればよいですか? 現在、同様のロジックを使用して、次の正しいシングルトン インスタンスを返していますlog file

#include <string>
#include <map>    

class Log {
  public:
    static Log* get_instance(std::string path);
  protected:
    Log(std::string path) : path(path), log(path) {};
    std::string path;
    std::ostream log;
  private:
    static std::map<std::string, Log*> instances;
};

std::map<std::string, Log*> Log::instances = std::map<std::string, Log*>();

Log* Log::get_instance(std::string path) {
  if (instances[path] == nullptr) {
    instances[path] = new Log(path);
  }
  return instances[path];
}

1 つの解決策は、これに似たものを使用して、map. ただし、私の理解では、これによりルックアップの複雑さがO(n)代わりにO(1). これは私のシナリオではそれほど大きな問題ではありません (ほんの一握りのログしかありません) が、より良い解決策は、デフォルトLog*で参照する型のポインターを強制することで、ルックアップ チェックと移植性を同時に行うことです。時間。これは可能ですか?もしそうなら、どうすればいいですか?nullptrO(1)

4

2 に答える 2

9

マップは常にそのメンバーを値初期化し(もちろん、メンバーがコピー初期化されていない状況で)、組み込み型の値初期化はゼロ初期化を意味するため、実際に定義された動作です。operator[]これは、それを呼び出す前に存在していなかった要素にアクセスするときに生成される新しいキーの値の部分に特に当てはまります。

ただし、初期化されていないポインタは必ずしもnullポインタではないことに注意してください。実際、その値を読み取るだけで、未定義の動作がすでに呼び出されます(特定の状況では、特定のプラットフォームでセグメンテーション違反が発生する可能性があります)。重要なのは、マップ内のポインターが初期化されていないということです。だからあなたが例えば書くなら

void foo()
{
  TestClass* p;
  // ...
}

pに初期化されませんnullptr

ただし、不要なエントリが蓄積されないように、代わりにプレゼンスを確認することをお勧めします。findメンバー関数を使用してプレゼンスを確認します。

map<int, TestClass*>::iterator it = TestMap.find(203);
if (it == map.end())
{
  // there's no such element in the map
}
else
{
  TestClass* p = it->second;
  // ...
}
于 2012-09-22T20:03:01.497 に答える
1

はい、それは定義された動作です。を介して要素にアクセスしたときに要素がまだマップにない場合、その要素はoperator[]デフォルトで作成されます。

于 2012-09-22T20:03:56.860 に答える