Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
300 views
in Technique[技术] by (71.8m points)

c++ - Is a default value of nullptr in a map of pointers defined behaviour?

The following code seems to always follow the true branch.

#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;
}

Is it defined behaviour for an uninitialized pointer to point at nullptr, or an artifact of my compiler?

If not, how can I ensure portability of the following code? Currently, I'm using similar logic to return the correct singleton instance for 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];
}

One solution would be to use something similar to this where you use a special function provide a default value when checking a map. However, my understanding is that this would cause the complexity of the lookup to be O(n) instead of O(1). This isn't too much of an issue in my scenario (there would only ever be a handful of logs), but a better solution would be somehow to force pointers of type Log* to reference nullptr by default thus making the lookup check O(1) and portable at the same time. Is this possible and if so, how would I do it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The map always value-initializes its members (in situations where they are not copy-initialized, of course), and value-initialization for builtin types means zero-initialization, therefore it is indeed defined behaviour. This is especially true for the value part of new keys generated when accessing elements with operator[] which didn't exist before calling that.

Note however that an uninizialized pointer is not necessarily a null pointer; indeed, just reading its value already invokes undefined behaviour (and might case a segmentation fault on certain platforms under certain circumstances). The point is that pointers in maps are not uninitialized. So if you write for example

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

p will not be initialized to nullptr.

Note however that you might want to check for presence instead, to avoid accumulating unnecessary entries. You'd check for presence using the find member function:

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;
  // ...
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...