Следующий код, кажется, всегда следует за истинной ветвью.
#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
или артефакт моего компилятора?
Если нет, как я могу обеспечить переносимость следующего кода? В настоящее время я использую похожую логику, чтобы вернуть правильный экземпляр Singleton для 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)
и портативный одновременно. Возможно ли это, и если да, то как бы я это сделал?
Карта всегда инициализирует значения своих членов (конечно, в ситуациях, когда они не инициализируются копированием), а инициализация значения для встроенных типов означает инициализацию с нуля, поэтому это действительно определенное поведение. Это особенно верно для части значения новых ключей, генерируемых при доступе к элементам с 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;
// ...
}
Да, это определенное поведение. Если элемент еще не находится на карте, когда вы получаете к нему доступ через operator[]
, он получает по умолчанию построен.