我的代码审阅者已经指出,使用地图的operator []是非常糟糕的,导致错误:
map[i] = new someClass; // potential dangling pointer when executed twice
要么
if (map[i]==NULL) ... // implicitly create the entry i in the map
虽然我在阅读API后看到insert()更好的风险,因为它检查重复,因此可以避免悬挂指针发生,我不明白如果处理正确,为什么[]不能用于所有?
我选择地图作为我的内部容器,正是因为我想使用其快速和自我解释的索引功能.
我希望有人可以和我争辩更多,或站在我身边:)
解决方法
当你想要设置一个键的值(如果已经有一个值覆盖它),那么operator []可以使用的唯一时间(我可以想到),而且你知道覆盖是安全的它应该是因为你应该使用智能指针,而不是原始指针),并且是廉价的默认构造,在某些上下文中,该值应该具有无投掷构造和赋值.
例如(类似于你的第一个例子)
std::map<int,std::unique_ptr<int>> m; m[3] = std::unique_ptr<int>(new int(5)); m[3] = std::unique_ptr<int>(new int(3)); // No,it should be 3.
否则有几种方法可以根据上下文来做,但是我建议总是使用一般的解决方案(这样你就不能错了).
找到一个值并创建它,如果它不存在:
一般解决方案(推荐,因为它一直有效)
std::map<int,std::unique_ptr<int>> m; auto it = m.lower_bound(3); if(it == std::end(m) || m.key_comp()(3,it->first)) it = m.insert(it,std::make_pair(3,std::unique_ptr<int>(new int(3)));
2.价廉物美价廉
std::map<int,std::unique_ptr<int>> m; auto& obj = m[3]; // value is default constructed if it doesn't exists. if(!obj) { try { obj = std::unique_ptr<int>(new int(3)); // default constructed value is overwritten. } catch(...) { m.erase(3); throw; } }
3.以廉价的违约建筑和无投注价值
std::map<int,my_objecct> m; auto& obj = m[3]; // value is default constructed if it doesn't exists. if(!obj) obj = my_objecct(3);
注意:您可以轻松地将一般解决方案包含在帮助程序中:
template<typename T,typename F> typename T::iterator find_or_create(T& m,const typename T::key_type& key,const F& factory) { auto it = m.lower_bound(key); if(it == std::end(m) || m.key_comp()(key,it->first)) it = m.insert(it,std::make_pair(key,factory())); return it; } int main() { std::map<int,std::unique_ptr<int>> m; auto it = find_or_create(m,3,[] { return std::unique_ptr<int>(new int(3)); }); return 0; }
注意,我传递一个模板化的工厂方法,而不是一个创建大小写的值,这样,当找到该值并且不需要创建时,就没有开销.由于lambda作为模板参数传递,编译器可以选择内联它.