将unique_ptr与STL容器结合使用,能实现自动内存管理,避免泄漏,提升代码安全与健壮性。通过std::make_unique创建对象并用std::move转移所有权,容器元素的生命周期由unique_ptr自动管理,析构时自动释放资源。访问时使用->或*操作符,并建议先检查指针有效性。该模式遵循RAII原则,明确所有权,提供异常安全保证,且运行时开销极小,是现代C++推荐的最佳实践。

在C++中,将
unique_ptr
unique_ptr
要将
unique_ptr
unique_ptr
MyObject
std::vector<std::unique_ptr<MyObject>>
std::vector<MyObject*>
当我们向容器中添加元素时,通常会使用
std::make_unique
std::move
unique_ptr
unique_ptr
unique_ptr
delete
举个例子:
立即学习“C++免费学习笔记(深入)”;
#include <vector>
#include <memory> // For std::unique_ptr and std::make_unique
#include <iostream>
class MyResource {
public:
int id;
MyResource(int _id) : id(_id) {
std::cout << "MyResource " << id << " created.\n";
}
~MyResource() {
std::cout << "MyResource " << id << " destroyed.\n";
}
void do_something() const {
std::cout << "MyResource " << id << " doing something.\n";
}
};
int main() {
// 创建一个存储unique_ptr的vector
std::vector<std::unique_ptr<MyResource>> resources;
// 向容器中添加元素
// 使用std::make_unique创建对象,并通过std::move转移所有权
resources.push_back(std::make_unique<MyResource>(1));
resources.push_back(std::make_unique<MyResource>(2));
// 访问容器中的元素
for (const auto& res_ptr : resources) {
if (res_ptr) { // 检查指针是否有效
res_ptr->do_something();
}
}
// 从容器中移除元素,其所指对象会被自动销毁
if (!resources.empty()) {
std::cout << "Removing first resource...\n";
resources.erase(resources.begin());
}
// 当main函数结束,resources容器被销毁时,剩余的unique_ptr也会被销毁,
// 从而自动删除它们所管理的对象。
std::cout << "Main function ending.\n";
return 0;
}运行这段代码,你会看到
MyResource
我认为,将
unique_ptr
首先,最核心的便是RAII(Resource Acquisition Is Initialization)原则的完美体现。
unique_ptr
unique_ptr
unique_ptr
unique_ptr
其次,所有权语义变得异常清晰。
unique_ptr
unique_ptr
std::move
unique_ptr
delete
再者,它提供了强大的异常安全保证。考虑一个向容器中插入多个动态创建对象的场景。如果使用裸指针,在创建了几个对象后,后续的对象创建失败或者其他操作抛出异常,那么之前成功创建的对象可能就无法被正确清理,导致内存泄漏。而
unique_ptr
unique_ptr
最后,性能开销几乎可以忽略不计。
unique_ptr
shared_ptr
unique_ptr
所以,从我个人的经验来看,只要你确定一个对象应该被独占所有权,并且它的生命周期需要与容器的元素生命周期绑定,那么
std::vector<std::unique_ptr<T>>
std::map<Key, std::unique_ptr<T>>
处理
unique_ptr
unique_ptr
std::move
所有权转移(std::move
当你需要将一个
unique_ptr
std::move
向容器中添加元素: 通常,你会创建一个临时的
unique_ptr
std::move
push_back
insert
emplace_back
std::vector<std::unique_ptr<MyResource>> resources;
// 方式一:直接创建并移动
resources.push_back(std::make_unique<MyResource>(3));
// 方式二:先创建,再移动
auto temp_ptr = std::make_unique<MyResource>(4);
resources.push_back(std::move(temp_ptr)); // temp_ptr 在这之后就为空了
// std::cout << temp_ptr.get(); // 输出0x0,证明已为空
// 对于map或set等关联容器
std::map<int, std::unique_ptr<MyResource>> resource_map;
resource_map.emplace(5, std::make_unique<MyResource>(5)); // emplace通常更高效
resource_map.insert({6, std::make_unique<MyResource>(6)}); // 也可以这样值得注意的是,
std::make_unique
push_back(std::make_unique(...))
unique_ptr
std::move
从容器中取出元素并转移所有权: 如果你需要从容器中“取出”一个元素,并且希望外部代码接管其所有权,同样需要使用
std::move
unique_ptr
nullptr
std::unique_ptr<MyResource> extracted_resource = std::move(resources[0]);
// 此时 resources[0] 变为空指针
if (resources[0] == nullptr) {
std::cout << "resources[0] is now null.\n";
}
extracted_resource->do_something(); // 可以正常使用 extracted_resource这种操作在设计工厂函数或者需要将容器中的对象“弹出”并交给其他模块管理时非常有用。
*元素访问(->
访问
unique_ptr
*
->
通过索引或迭代器访问: 无论是
std::vector
std::deque
unique_ptr
// 向量通过索引访问
if (resources[0]) { // 最好先检查是否为空
resources[0]->do_something(); // 使用 -> 访问成员函数
std::cout << "ID: " << (*resources[0]).id << "\n"; // 使用 * 解引用后访问成员
}
// 迭代器访问
for (auto it = resource_map.begin(); it != resource_map.end(); ++it) {
if (it->second) { // map的value是unique_ptr
it->second->do_something();
}
}
// C++11 range-based for loop
for (const auto& res_ptr : resources) { // 注意这里是 const auto&,避免不必要的移动
if (res_ptr) {
res_ptr->do_something();
}
}务必记住,在解引用
unique_ptr
unique_ptr
unique_ptr
总结来说,
std::move
unique_ptr
->
*
unique_ptr
即便
unique_ptr
unique_ptr
常见的陷阱:
忘记std::move
unique_ptr
std::move
unique_ptr
unique_ptr
push_back
std::unique_ptr<MyResource> p1 = std::make_unique<MyResource>(7); // std::unique_ptr<MyResource> p2 = p1; // 编译错误:call to deleted constructor // resources.push_back(p1); // 编译错误:call to deleted constructor resources.push_back(std::move(p1)); // 正确
记住,只有右值(例如
std::make_unique
std::move
unique_ptr
混合裸指针与智能指针: 当你的代码库同时存在裸指针和智能指针时,很容易导致所有权混乱。例如,你从一个
unique_ptr
get()
delete
unique_ptr
delete
std::unique_ptr<MyResource> p = std::make_unique<MyResource>(8); MyResource* raw_p = p.get(); // delete raw_p; // 绝对不要这样做!会导致双重释放
一旦资源由
unique_ptr
unique_ptr
在容器中存储unique_ptr
std::vector<std::unique_ptr<MyResource>>
std::vector<MyResource*>
std::vector<MyResource&>
std::vector<std::unique_ptr<MyResource>> smart_resources; smart_resources.push_back(std::make_unique<MyResource>(9)); // 错误的做法:存储裸指针,当smart_resources元素被移除或smart_resources销毁时,这些裸指针会悬空 std::vector<MyResource*> raw_pointers; raw_pointers.push_back(smart_resources[0].get()); // 此时如果smart_resources[0]被移除或smart_resources超出作用域,raw_pointers[0]就成了悬空指针
如果确实需要一个辅助容器来“观察”智能指针管理的对象,那么存储裸指针是可行的,但你必须清楚地知道这些裸指针的生命周期完全依赖于原始的智能指针容器。一旦原始容器中的智能指针被销毁,对应的裸指针就失效了。
不当的自定义删除器:
unique_ptr
性能考量:
std::make_unique
std::make_unique
unique_ptr
new
unique_ptr
std::make_unique
new MyObject()
unique_ptr<MyObject>(raw_ptr)
new
unique_ptr
raw_ptr
unique_ptr
make_unique
unique_ptr
unique_ptr
shared_ptr
unique_ptr
unique_ptr
shared_ptr
容器操作的开销:
std::vector
std::vector
unique_ptr
以上就是C++unique_ptr与STL容器结合使用技巧的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号