<p>std::unique对指针vector无效,因其仅比较指针地址而非所指内容,不自动解引用;需配合自定义谓词(如 a == b)并先排序,或改用set/unordered_set按值去重。</p>

为什么 std::unique 对指针 vector 无效
因为 std::unique 只比较相邻元素的值是否相等,而两个不同地址的 int* 指针即使指向相同内容,其指针值(地址)也不同,所以不会被判定为重复。更关键的是,它根本不解引用——压根不看指针指向的内容。
直接对 std::vector<int></int> 调用 std::unique + erase,只会去重“相同地址”的指针,不是你想要的“指向相同值”的去重。
手动实现深度去重:先解引用再比较
核心思路是:把指针指向的实际值提取出来,按值去重,同时保留原始指针(或安全转移所有权)。常见场景是管理堆上对象,且需避免内存泄漏。
- 若指针指向 POD 类型(如
int、double),可先收集值,用std::set或std::unordered_set去重,再重建指针(注意:必须 new 新对象,不能复用原指针,否则原指针仍需 delete) - 若指针指向自定义类型,确保该类型重载了
operator==和哈希(用于 unordered_set)或满足严格弱序(用于 set) - 务必配对使用
delete清理旧指针,否则内存泄漏;若用std::unique_ptr更安全,但此时 vector 存的是智能指针,去重逻辑不变,只是自动析构
示例(原始裸指针):
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
std::vector<int*> ptrs = {new int(1), new int(2), new int(1), new int(3)};
std::vector<int*> unique_ptrs;
std::set<int> seen;
<p>for (int<em> p : ptrs) {
if (p && seen.find(</em>p) == seen.end()) {
seen.insert(<em>p);
unique_ptrs.push_back(new int(</em>p)); // 深拷贝
}
}
// 记得清理原始 ptrs
for (int* p : ptrs) delete p;
用 std::unique_ptr 避免手动内存管理
裸指针深度去重本质是“值语义 + 资源管理”,而 std::unique_ptr 天然支持移动语义,能简化逻辑并杜绝泄漏。
- 先按解引用值排序(
std::sort配合 lambda:[&](const auto& a, const auto& b) { return *a < *b; }) - 再用
std::unique(此时需自定义谓词:[&](const auto& a, const auto& b) { return *a == *b; }) - 最后
erase移除重复项——unique返回新尾迭代器,移动语义保证资源不丢
示例:
std::vector<std::unique_ptr<int>> ptrs;
ptrs.push_back(std::make_unique<int>(1));
ptrs.push_back(std::make_unique<int>(2));
ptrs.push_back(std::make_unique<int>(1));
ptrs.push_back(std::make_unique<int>(3));
<p>std::sort(ptrs.begin(), ptrs.end(), [](const auto& a, const auto& b) { return <em>a < </em>b; });
auto last = std::unique(ptrs.begin(), ptrs.end(), [](const auto& a, const auto& b) { return <em>a == </em>b; });
ptrs.erase(last, ptrs.end());
性能和边界要注意什么
深度去重成本取决于解引用开销和比较复杂度。对大对象,解引用本身可能很重;对频繁调用场景,应避免反复 new/delete。
- 如果对象很大,考虑只存
std::shared_ptr并用std::set<T, std::less<>>去重,利用指针值唯一性 + 自定义比较器按内容比 - 空指针必须显式检查(
p == nullptr),否则*p解引用崩溃 -
std::unique要求输入已排序(按去重依据),否则行为未定义;漏掉排序是常见 bug - 多线程环境下,若 vector 和所指对象都可能被并发访问,需额外同步,仅容器操作线程安全不等于数据安全
真正麻烦的从来不是怎么写,而是谁负责释放、何时释放、有没有别处还在用这些指针——裸指针的生命周期模糊性,比算法本身更值得花时间厘清。

















