关于std::map中的find和[]的问题

本文探讨了std::map中的find与operator[]的区别,并通过示例代码展示了如何避免潜在的bug,强调了正确使用std::map的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 std::map不是顺序容器,这应该都知道的,map中重载了operator []操作符,可以用map的键值索引到相关的数据,但是这个[]和一般数组的[]可不一样。有的人又认为既然[]就是利用下标做索引和std::map中的find用键值索引是不是一样的,所以有的会写出下面类似的代码:

std::map<int,int*> mapTemp;

...

int* Find1(int i)

{

    if( mapTemp.end() == mapTemp.find(i) )

        return NULL;

    return mapTemp[i].second;

}

上面代码有什么问题,看看operator []的源码

mapped_type& operator[](const key_type& _Keyval)
  { // find element matching _Keyval or insert with default mapped
  iterator _Where = this->lower_bound(_Keyval);
  if (_Where == this->end()
   || this->comp(_Keyval, this->_Key(_Where._Mynode())))
   _Where = this->insert(_Where,
    value_type(_Keyval, mapped_type()));
  return ((*_Where).second);
  }

再看看find的源码

const_iterator find(const key_type& _Keyval) const
  { // find an element in nonmutable sequence that matches _Keyval
  const_iterator _Where = lower_bound(_Keyval);
  return (_Where == end()
   || _DEBUG_LT_PRED(this->comp,
    _Keyval, _Key(_Where._Mynode()))
     ? end() : _Where);
  }

对比上面黑色加粗的部分应该知道了吧,就是Find1中你重复找了两遍,标准的写法应该是

int* Find2(int i)

{

    std::map<int,int*>::iterator iter = mapTemp.find(i);

    if( mapTemp.end() != iter )

        return iter->second;

    return NULL;

}

其实Find1还好,就是多查了一遍,至少还判了索引的有效值,如果写成下面这样

int* Find3(int i)

{

    return mapTemp[i].second;

}

那就悲剧了,再看看operator []的源码红色加粗的部分,[]操作符再没有找到该键值索引值的时候会插入一个键值索引为i的东西,这东西要是不了解bug查起来肯定会比较头痛的。

记录警示之

### 使用 `std::find_if` `std::map` 在 C++ 中,`std::map` 是一种关联容器,它存储键值对并按键自动排序。虽然可以直接通过 `operator[]` 或者 `.at()` 方法访问特定的键值对,但在某些场景下可能需要更复杂的条件来查找某个元素。此时可以借助标准库中的算法函数 `std::find_if` 来完成。 #### 实现方式 尽管 `std::map` 提供了自己的查找方法(如 `.find`, `.count`, `.lower_bound` 等),但这些方法通常只支持基于键的精确匹配。如果希望执行自定义逻辑的查找,则可以结合 `std::find_if` 函数实现这一目标。以下是具体用法: 1. **迭代器范围** 需要提供一个有效的迭代器范围给 `std::find_if`,对于 `std::map` 可以使用其成员函数 `.begin()` `.end()` 获取整个映射的范围。 2. **谓词函数** 定义一个返回布尔类型的函数或者 lambda 表达式作为谓词参数传入 `std::find_if`,该谓词用于判断当前遍历到的元素是否满足指定条件。 3. **调用形式** 将上述两个部分组合起来即可形成完整的调用语句。 下面是具体的代码示例[^1]: ```cpp #include <iostream> #include <map> int main() { std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}, {3, "three"}}; // 查找 value 值长度大于 3 的第一个元素 auto result = std::find_if(myMap.begin(), myMap.end(), [](const std::pair<const int, std::string>& elem) -> bool { return elem.second.length() > 3; }); if (result != myMap.end()) { std::cout << "Found element with key=" << result->first << ", value='" << result->second << "'\n"; } else { std::cout << "No matching element found.\n"; } return 0; } ``` 在这个例子中,我们创建了一个简单的整数到字符串的映射表,并尝试找到其中具有较长字符串值的第一个条目。注意这里使用的 Lambda 表达式的捕获列表为空(`[]`)表示不捕捉外部变量;而形参类型则显式声明为 const 引用以便提高性能同时避免不必要的拷贝操作。 另外需要注意的是,在实际应用过程中应当始终验证由 `std::find_if` 返回的结果指针是否等于结束标记(end iterator),以此确认是否存在符合条件的目标对象。 --- ### 性能考量 当涉及到频繁查询时,应该考虑到不同数据结构之间的差异以及它们各自适用的最佳实践。例如,相比于单纯依靠线性扫描的方式来说,利用内置二分搜索机制的标准 STL 关联型容器往往能够带来更好的时间复杂度表现——即 O(log n)[^4]。然而一旦引入额外约束条件之后,就不得不退回到通用序列处理模式下了,这时候整体效率可能会有所下降取决于输入规模大小等因素影响。 因此建议开发者权衡好需求优先级后再做决定:如果是简单属性过滤的话继续沿用原生接口会更加高效简洁明了些;反之遇到较为特殊的情况再考虑混合编程风格也不迟。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值