C++20 推荐用 std::erase(s, c) 原地删除所有指定字符;老标准用 s.erase(std::remove(s.begin(), s.end(), c), s.end());不改原串则用 std::copy_if 构造新串;避免 find+erase 循环。

用 std::erase(C++20)最直接
如果你的编译器支持 C++20,std::erase 是删除字符串中所有指定字符的首选——它原地修改、语义清晰、不用手写循环。std::erase 对 std::string 有重载,直接传入要删的字符即可:
std::string s = "a*b*c*d"; std::erase(s, '*');
执行后 s 变成 "abcd"。注意这不是算法函数,而是容器专属的便捷函数,不会返回迭代器,也不需要 erase–remove 惯用法。
容易踩的坑:std::erase 在 C++20 才正式加入标准库;若用 GCC 编译,需加 -std=c++20,且 libstdc++ 版本不能太旧(GCC 11+ 默认支持)。Clang 12+ 和 MSVC 19.30+ 也已支持。
兼容 C++11/14/17:用 erase–remove 惯用法
在老标准下,得靠 std::remove + std::string::erase 组合实现“逻辑删除 + 物理收缩”:
立即学习“C++免费学习笔记(深入)”;
std::string s = "a*b*c*d"; s.erase(std::remove(s.begin(), s.end(), '*'), s.end());
std::remove 把所有非 '*' 元素前移,返回新逻辑尾迭代器;erase 从该位置删到末尾。这是标准做法,性能好、无额外内存分配。
常见错误现象:
- 忘记调用 erase,只写 std::remove → 字符串长度不变,末尾残留脏数据
- 误用 std::remove_if 而不写谓词 → 编译失败
- 对 const std::string& 尝试调用 erase → 编译错误,必须是可修改的左值
想保留原字符串?用 std::copy_if 构造新串
如果不想改动原串,或者需要更灵活的过滤逻辑(比如删星号但保留两个连续星号),推荐用 std::copy_if 写入新字符串:
std::string s = "a**b*c";
std::string out;
out.reserve(s.size()); // 预分配避免多次 realloc
std::copy_if(s.begin(), s.end(), std::back_inserter(out),
[](char c) { return c != '*'; });这样 out 就是干净结果。reserve 很关键——否则每次 push_back 可能触发扩容,影响性能。
使用场景:
- 原串是 const 或你不希望修改它
- 后续还要用原串做别的事
- 删除条件复杂(如“删单个星号,但跳过 **”),这时把 lambda 改成状态机式判断就行
别用 find + erase 循环删
有人会这么写:
while (s.find('*') != std::string::npos) {
s.erase(s.find('*'), 1);
}看起来直观,但实际很慢:每次 find 都从头扫,erase 每次都移动后面所有字符。时间复杂度接近 O(n²),字符串越长越明显。
性能影响:
- 对 10KB 字符串,比 erase–remove 慢 10–50 倍
- 迭代器失效风险:find 返回的 pos 在 erase 后可能越界,尤其空串时 find 返回 npos,但若逻辑写错易崩溃
- 无法处理嵌套或上下文相关逻辑(比如只删行首星号),扩展性差
真正需要逐个定位时(例如只删第 3 个星号),才考虑 find,否则一律优先用前三种方式。
最常被忽略的是标准版本兼容性——很多人写了 std::erase 却没开 C++20,编译直接报错;而 erase–remove 看似多写两行,却能在所有现代编译器上零配置运行。


















