使用auto避免不必要的拷贝开销

map<string, int> word_count;

for (const auto& kv : word_count) {
}

// C++17
for (const auto& [word, count] : word_count) {
}

如果你想把auto的类型写出来,一定不要忘了key是const。如果你写成pair<string, int>,会造成额外的拷贝开销:

for (const std::pair<const std::string, int>& kv : word_count) {
}

// 产生拷贝开销
for (const std::pair<std::string, int>& kv : word_count) {
}

容器自定义类型的移动构造函数声明为noexcept

std::vector<T>在进行扩容的时候,如果T的移动构造函数没有声明为noexcept且STL没有禁用异常的话,在扩容过程中不会调用移动构造函数,而是拷贝构造函数。

减少隐性的重复操作

for(auto & key : vec) {
    auto it = map.find(key);
    if (map.find(key) != map.end()) {
        cout << map[key] << std::endl;
    }
}

上面代码会进行两次查找,第一次是find,第二次是operator[]或者at(),所以要避免这种隐性的重复操作,改成下面这样:

for(auto & key : vec) {
    auto it = map.find(key);
    if (it != map.end()) {
        cout << it->second << std::endl;
    }
}

clear的时间复杂度不是O(1)

不管是序列容器还是关联容器,clear的时间复杂度都是O(n),因为需要逐个析构每个元素。

sort给自定义对象排序,可能存在拷贝开销

当自定义类型没有移动构造函数时,sort()排序时调用的是拷贝构造函数。

所以,使用sort()时,最好给自定义对象添加移动构造函数和移动赋值运算符。

size() - 1

size()返回的是无符号类型,如果容器为空,size() - 1会导致无符号数下溢,变成一个很大的数。导致死循环。

// 正确
for (size_t i = 0; i < vec.size(); ++i) {
    cout << vec[i] << endl;
}

// 错误,当vecto为空时,死循环
for (size_t i = 0; i <= vec.size() - 1; ++i) {
    cout << vec[i] << endl;
}

// 错误
for (size_t i = vec.size() - 1; i >= 0; i--) {
    cout << vec[i] << endl;
}