C++ STL 使用注意事项整理

Intro

简单整理了一些关于 C++ STL 的注意点。欢迎各路神仙在评论区提出意见。

虽然大多数东西可以手写不过某些东西最好少造轮子,善用 STL 可以节约很多考场时间,简化实现。

当然是时空限制和功能足够的前提下。

Tips

  1. 容易忽略的 C++11/17 可用但 C++98/14 不可用的特性:
    • 关联式容器(std::set/map/unordered_set/unordered_map)的 erase(it) 在 C++11 中提供了返回值,返回的是迭代器指向删除位置的下一个迭代器。
    • std::priority_queue 中的 swap 方法在 C++11 中是 \(O(1)\)​ 的,但 C++98 只能用常规的 std::swap(q1, q1) 的方式交换,而这样的复杂度是与优先队列的大小相关的。
    • 使用初始化列表(大括号,{...})初始化容器的方式一般只在 C++11 中被支持。
    • 所有 emplace 系的函数一般只在 C++11 中被支持。
    • std::vector/deque/string 中的 shrink_to_fit 函数只在 C++11 中被支持。
    • std::string 中的 pop_back/front/back 只在 C++11 中被支持。
    • 所有容器的 cbegin/cend/crbegin/crend 都只在 C++11 中被支持。
    • UPD:至 CSP2021 起,编译标准为 C++14。
    • 形如 auto [x, y] = p 的方法是 C++17 中支持的(可能直接编译可以通过只有 warning 但不推荐)。
  2. bool 数组是如果发现空间不是很对可以考虑 std::bitsetstd::vector<bool>。不过仅仅是单次访问 std::bitset 没有 bool 数组快。
  3. 常数较大的一些 STL 容器:std::stack/std::queue/std::deque/std::list/std::valarray/std::string...,这些东西能手写尽量手写。
  4. std::multisetcount() 函数的复杂度为 \(O(\log n + ans)\),因此多多考虑用 std::map 实现 std::multiset 的功能。
  5. 直接对 std::set/std::multiset/std::map/std::multimap 进行 <algorithm> 库中的二分查找的复杂度为 \(O(n)\),正解应该用其自带的 lower_bound()/upper_bound() 函数,复杂度为 \(O(\log n)\)
  6. std::priority_queue 默认为大根堆,小根堆可以取负、重载运算符或 std::greater
  7. std::multimap 不支持 [] 下标访问。
  8. 在使用 std::vector 时可以尝试用 reserve() 预测数组的大小以提高效率。实测 push_back() 次数较多时 reserve() 后能快一倍。
  9. 许多 STL 容器都特化了 swap() 函数,可以实现 \(O(1)\) 交换。
  10. std::bitset 重载了流运算符,即可以用 cin/cout 输入输出。当然也可以用这个输出整数的二进制。
  11. std::nth_element 会改变元素在容器中的位置。
  12. std::vector 等序列容器进行下标访问是最好将 size() 函数的返回值转为 int。一个成功避免大问题的一个例子:for (int i = (int)v.size() - 2; i >= 0; i--) v[i] += v[i + 1]
  13. 使用 std::map<Key, Value>[] 下标访问方法是,如果无对应 Key 元素的话会新建一个,值为调用构造函数 Value() 的结果。
  14. STL 容器进行修改操作时需要注意迭代器被非法化的情况,被非法化的迭代器不能再使用。如使用 std::seterase(it)it 就被非法化了。std::vector 等也需要注意这种问题。各种容器增删操作非法化情况表
  15. 判断容器是否为空尽量使用 empty() 而不是 size() == 0,因为某些容器的 size() 函数的复杂度并不是 \(O(1)\)(如一些 list 实现)。
  16. 使用排序 STL 时,比较函数必须满足 严格弱序,否则可能出现玄学错误。
  17. 对迭代器 it+ a - b 的操作尽量写成 it + (a - b) 而不是 it + a - b,因为 it + a 可能出现越界情况。
  18. __gnu_cxx::rope 中的 erase(size_t __p) 是错(编写者锅了)的!它的注释中写的是 “Erase, single character”,但实际上写的是 erase(__p, __p + 1),那么你实际上会把 __p 开始的 __p + 1 个位置全部删除。
  19. 一个小 trick:代码里出现一大坨 begin/end 是不是很烦?你可以 #define all(x) (x).begin(), (x).end()。要注意 (x) 会算两边,如果是临时算出来的不应该这么做。
posted @ 2020-12-03 15:19  -Wallace-  阅读(970)  评论(3编辑  收藏  举报