unique_ptr通过独占所有权和RAII原则,确保动态资源在作用域结束时自动释放,避免内存泄漏、双重释放和异常安全问题;使用std::make_unique可保证异常安全并简化代码;其移动语义支持所有权转移,适用于函数返回、参数传递和容器存储,提升代码安全与效率。

C++的
unique_ptr
unique_ptr
delete
使用
unique_ptr
1. 创建unique_ptr
std::make_unique
#include <memory>
#include <iostream>
class MyObject {
public:
MyObject() { std::cout << "MyObject created\n"; }
~MyObject() { std::cout << "MyObject destroyed\n"; }
void doSomething() { std::cout << "Doing something...\n"; }
};
// 使用 std::make_unique 创建
std::unique_ptr<MyObject> ptr1 = std::make_unique<MyObject>();
// 也可以直接在声明时初始化
// auto ptr2 = std::make_unique<MyObject>(); // C++14 以后避免直接使用
new
// 不推荐,但可以工作 std::unique_ptr<MyObject> ptr_bad_practice(new MyObject());
2. 访问所指向的对象: 和原始指针一样,可以使用解引用运算符
*
->
unique_ptr
ptr1->doSomething(); // 调用 MyObject 的成员函数 (*ptr1).doSomething(); // 另一种方式
如果你需要获取原始指针(通常是为了与C风格API交互,但请谨慎使用,因为这可能绕过智能指针的安全性),可以使用
get()
立即学习“C++免费学习笔记(深入)”;
MyObject* rawPtr = ptr1.get();
if (rawPtr) {
rawPtr->doSomething();
}3. 转移所有权:
unique_ptr
std::move
unique_ptr
nullptr
std::unique_ptr<MyObject> ptrA = std::make_unique<MyObject>();
std::unique_ptr<MyObject> ptrB = std::move(ptrA); // 所有权从 ptrA 转移到 ptrB
if (ptrA) { // ptrA 现在是空的
std::cout << "ptrA still holds an object.\n";
} else {
std::cout << "ptrA is now empty.\n"; // 会打印这行
}
ptrB->doSomething(); // ptrB 现在拥有对象这种转移所有权的能力在函数返回、或者将对象放入容器时非常有用。
4. 释放或重置:
reset()
unique_ptr
nullptr
std::unique_ptr<MyObject> ptrC = std::make_unique<MyObject>(); ptrC.reset(); // MyObject 被销毁,ptrC 变空 ptrC.reset(new MyObject()); // 销毁旧对象(如果存在),接管新对象
release()
std::unique_ptr<MyObject> ptrD = std::make_unique<MyObject>(); MyObject* releasedPtr = ptrD.release(); // ptrD 变空,MyObject 未被销毁 // 此时,你必须手动 delete releasedPtr delete releasedPtr;
release()
5. 自定义删除器(Custom Deleter):
unique_ptr
delete
new
malloc
// 示例:自定义删除器,用于关闭文件句柄
struct FileCloser {
void operator()(FILE* fp) const {
if (fp) {
fclose(fp);
std::cout << "File closed.\n";
}
}
};
// 使用 unique_ptr 管理 FILE*
std::unique_ptr<FILE, FileCloser> filePtr(fopen("example.txt", "w"));
if (filePtr) {
fputs("Hello, unique_ptr!\n", filePtr.get());
// 文件会在 filePtr 超出作用域时自动关闭
}自定义删除器可以是函数指针、函数对象或Lambda表达式。这让
unique_ptr
unique_ptr
在我看来,
unique_ptr
new
delete
delete
unique_ptr
内存泄漏的终结者: 这是最直接、最显著的优点。
unique_ptr
new
make_unique
unique_ptr
delete
双重释放(Double Deletion)的预防: 裸指针的另一个陷阱是,如果同一个内存地址被
delete
unique_ptr
unique_ptr
unique_ptr
异常安全性的提升: 想象一下,在一个函数中,你
new
delete
delete
unique_ptr
所有权语义的明确: 当函数参数或返回值是裸指针时,我们很难一眼看出这个指针的所有权归属:是调用者负责释放?还是被调用者负责释放?
unique_ptr
unique_ptr
unique_ptr
这些问题,在传统的C++编程中,往往需要大量的约定、注释和严谨的代码审查来避免。而
unique_ptr
std::make_unique
new
在现代C++编程中,我几乎可以毫不犹豫地说:总是优先考虑std::make_unique
std::make_shared
new
让我们看一个经典的例子来理解其中的差异。假设你有一个函数
process(std::unique_ptr<A> a, std::unique_ptr<B> b)
// 场景一:使用 new process(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())); // 场景二:使用 make_unique process(std::make_unique<A>(), std::make_unique<B>());
在场景一中,编译器在执行
process
new A()
new B()
std::unique_ptr
new A()
new B()
std::unique_ptr<A>
std::unique_ptr<B>
问题就出在这里。如果
new A()
new B()
new A()
std::unique_ptr<A>
std::unique_ptr<A>
而场景二,使用
std::make_unique
unique_ptr
std::make_unique<A>()
unique_ptr
std::make_unique<B>()
std::make_unique<A>()
process
unique_ptr
除了异常安全,
std::make_unique
<>
()
shared_ptr
shared_ptr
std::make_shared
new
new
unique_ptr
shared_ptr
所以,我的建议是,除非你真的有非常特殊的原因(比如需要自定义分配器,而
make_unique
unique_ptr
std::make_unique
unique_ptr
unique_ptr
unique_ptr
1. 函数返回unique_ptr
unique_ptr
std::unique_ptr<MyObject> createMyObject() {
// 假设这里有一些复杂的初始化逻辑
std::cout << "Creating object inside function...\n";
return std::make_unique<MyObject>(); // 返回一个 unique_ptr
}
int main() {
std::unique_ptr<MyObject> obj = createMyObject(); // 所有权从函数内部转移到 main
obj->doSomething();
// main 函数结束时,obj 会自动销毁 MyObject
return 0;
}在这里,
createMyObject
MyObject
unique_ptr
main
obj
delete
MyObject
2. 将unique_ptr
std::move
unique_ptr
void takeOwnership(std::unique_ptr<MyObject> obj) {
if (obj) {
std::cout << "Function received ownership, doing something...\n";
obj->doSomething();
// 函数结束时,obj 会自动销毁 MyObject
}
}
int main() {
std::unique_ptr<MyObject> myObj = std::make_unique<MyObject>();
takeOwnership(std::move(myObj)); // 显式移动所有权
// 此时 myObj 已经为空
if (!myObj) {
std::cout << "Original unique_ptr is now empty.\n";
}
return 0;
}这种方式明确地表达了函数
takeOwnership
myObj
myObj
3. 在容器中存储动态对象:
std::vector<std::unique_ptr<T>>
unique_ptr
#include <vector>
int main() {
std::vector<std::unique_ptr<MyObject>> objects;
objects.push_back(std::make_unique<MyObject>()); // 移动构造
objects.emplace_back(); // 直接在容器中构造 MyObject,然后用 unique_ptr 封装
// 假设我们想添加一个已经存在的 unique_ptr
std::unique_ptr<MyObject> anotherObj = std::make_unique<MyObject>();
objects.push_back(std::move(anotherObj)); // 显式移动所有权
for (const auto& ptr : objects) {
if (ptr) {
ptr->doSomething();
}
}
// 当 vector 超出作用域时,其中所有的 unique_ptr 都会被销毁,
// 进而销毁它们所指向的 MyObject 实例。
return 0;
}这种方式避免了在容器中存储原始指针可能导致的内存泄漏问题,也避免了存储值类型对象可能带来的不必要的拷贝开销(如果
MyObject
总结一下,unique_ptr
unique_ptr
可以说,移动语义是
unique_ptr
以上就是C++智能指针 unique_ptr使用指南的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号