下面的代码似乎总是遵循真正的分支。
#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];
}
一个解决方案是 use something similar to this在检查 map
时,在使用特殊函数的地方提供默认值。但是,我的理解是,这会导致查找的复杂度为 O(n)
而不是 O(1)
。在我的场景中这不是什么大问题(只有少数日志),但更好的解决方案是以某种方式强制 Log*
类型的指针引用 默认情况下为 nullptr
,从而使查找检查 O(1)
并同时可移植。这可能吗?如果可以,我该怎么做?
最佳答案
映射总是对其成员进行值初始化(当然,在它们未进行复制初始化的情况下),内置类型的值初始化意味着零初始化,因此它确实是定义的行为。对于使用 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;
// ...
}
关于c++ - 指针映射中的默认值 nullptr 是否定义了行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12547234/