直接声明 std::list lst; 即初始化为空,应使用 lst.empty()(o(1)、语义清晰)而非 lst.size() == 0(可能 o(n))判断是否为空。

怎么初始化一个空的 std::list 并确认它真为空
直接声明就完事,但很多人用 size() 判断是否为空,其实效率低且不必要。empty() 才是标准、常数时间、语义清晰的写法。
常见错误:在循环里反复调用 size() 判断终止条件——某些实现下 size() 是 O(n),尤其老编译器或 debug 模式;而 empty() 永远是 O(1)。
-
std::list<int> lst;</int>—— 默认构造,已为空 - 检查是否为空,写
if (lst.empty()),别写if (lst.size() == 0) - 如果需要带初始值,
std::list<:string> names{"Alice", "Bob"};</:string>,注意不是用花括号初始化语法传入大小
插入元素时,push_back 和 emplace_back 差在哪
两者都插到尾部,但底层行为不同:前者构造对象再移动/拷贝进容器,后者直接在容器内存里原地构造。对复杂类型(比如含自定义构造函数的类),emplace_back 更高效、更安全。
容易踩的坑:用 emplace_back 传入临时对象(如 std::string("hello"))反而可能触发额外移动;真正该用的是直接传参数,让构造函数被转发。
立即学习“C++免费学习笔记(深入)”;
- 追加已存在对象:
lst.push_back(x); - 就地构造新对象:
lst.emplace_back(42, "world");(假设元素类型有对应构造函数) - 别写
lst.emplace_back(std::string("hi")),这等于多造一次临时对象;直接写lst.emplace_back("hi")即可
遍历 std::list 为什么不能用下标访问
std::list 是双向链表,不支持随机访问。任何试图用 lst[i] 或 lst.at(i) 的代码都会编译失败——因为这些操作符根本没定义。
你只能用迭代器或范围 for。想“跳到第 5 个元素”?必须从头或从尾一步步走,O(n) 时间。这不是 bug,是设计选择:换来了稳定的插入/删除性能,代价就是放弃下标。
- 正经遍历写法:
for (const auto& x : lst) { ... } - 需要索引时,别硬算:用
std::advance(it, n),但要清楚这是线性时间 - 如果频繁按位置访问,
std::list就不是合适容器——换std::vector或std::deque
删除元素后,迭代器失效规则和常见崩溃点
std::list 的迭代器稳定性极好:只有被 erase 的那个元素对应的迭代器失效,其余全部有效。这点比 vector 强太多,但也容易让人放松警惕。
最常崩的地方是:一边遍历一边删,却没处理好迭代器递进逻辑。用 erase 返回下一个有效迭代器,别自己 ++。
- 安全删除所有偶数:
for (auto it = lst.begin(); it != lst.end(); ) { if (*it % 2 == 0) it = lst.erase(it); else ++it; } - 千万别写
if (...) lst.erase(it); ++it;—— erase 后it已失效,再 ++ 就 UB -
clear()后所有迭代器失效,但容器本身仍可用
std::list 前,先问自己:真的需要在任意位置 O(1) 插删,且不关心访问模式吗?











