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,还是我的编译器的工件?

如果没有,如何确保以下代码的可移植性?目前,我正在使用类似的逻辑来为 a 返回正确的单例实例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];
}

一种解决方案是使用与此类似的方法,其中您使用特殊功能在检查时提供默认值map。但是,我的理解是,这会导致查找的复杂性O(n)变为O(1). 在我的场景中这不是什么大问题(只有少数日志),但更好的解决方案是强制类型指针默认Log*引用nullptr,从而使查找检查O(1)和可移植性相同时间。这可能吗?如果可以,我该怎么做?

4

2 回答 2

9

映射总是对其成员进行值初始化(当然,在它们没有被复制初始化的情况下),并且内置类型的值初始化意味着零初始化,因此它确实是定义的行为。operator[]对于在调用之前不存在的元素访问时生成的新键的值部分尤其如此。

但是请注意,未初始化的指针不一定是空指针;事实上,仅仅读取它的值已经调用了未定义的行为(并且在某些情况下可能会在某些平台上出现分段错误)。关键是地图中的指针不是未初始化的。所以如果你写例如

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 回答