4.2、std::unique_ptr/std::shared_ptr/std::weak_ptr(智能指针)
智能指针的设计哲学与核心价值
- • 自动管理资源生命周期,避免手动
delete
带来的错误。 - • 明确资源所有权,通过不同智能指针表达不同的所有权语义。
- • 异常安全,保证异常发生时资源正确释放。
- • 解决循环引用,通过
weak_ptr
打破shared_ptr
循环依赖。
智能指针不仅是内存管理工具,更是现代C++设计理念的体现,让资源管理成为语言级别的安全机制。
1. 新特性使用方法:基础示例与底层细节
std::unique_ptr -- 独占所有权,轻量高效
#include <iostream>
#include <memory>
class Resource {
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
void say() { std::cout << "Hello from Resource\n"; }
};
void uniquePtrDemo() {
std::unique_ptr<Resource> ptr = std::make_unique<Resource>();
ptr->say();
// ptr超出作用域时,自动调用Resource析构函数释放资源
}
int main() {
uniquePtrDemo();
return 0;
}
底层原理:unique_ptr
封装了裸指针,禁止复制构造和赋值,只支持移动语义。它的大小与裸指针相同,重载了operator*
和operator->
,使用时几乎无性能开销。资源释放由析构函数自动完成,保证异常安全。
std::shared_ptr -- 共享所有权,引用计数管理
#include <iostream>
#include <memory>
void sharedPtrDemo() {
auto sp1 = std::make_shared<Resource>();
{
std::shared_ptr<Resource> sp2 = sp1; // 引用计数+1
std::cout << "Use count inside block: " << sp1.use_count() << std::endl;
sp2->say();
} // sp2销毁,引用计数-1
std::cout << "Use count after block: " << sp1.use_count() << std::endl;
}
int main() {
sharedPtrDemo();
return 0;
}
底层原理:shared_ptr
内部维护一个引用计数控制块,所有指向同一资源的shared_ptr
共享该计数。计数通过原子操作保证线程安全。最后一个shared_ptr
销毁时释放资源和控制块。
std::weak_ptr -- 弱引用,解决循环引用
#include <iostream>
#include <memory>
struct Node {
std::shared_ptr<Node> next;
std::weak_ptr<Node> prev; // 弱引用,避免循环引用
~Node() { std::cout << "Node destroyed\n"; }
};
void weakPtrDemo() {
auto n1 = std::make_shared<Node>();
auto n2 = std::make_shared<Node>();
n1->next = n2;
n2->prev = n1; // weak_ptr不增加引用计数,打破循环引用
}
int main() {
weakPtrDemo();
return 0;
}
底层原理:weak_ptr
不拥有资源,不增加引用计数。它持有一个指向shared_ptr
控制块的弱引用,可以检测资源是否仍存在。通过lock()
尝试获取shared_ptr
,安全访问资源,避免悬空指针。
2. 进阶用法:自定义删除器与enable_shared_from_this
自定义删除器
#include <iostream>
#include <memory>
#include <cstdio>
struct FileCloser {
void operator()(FILE* fp) const {
if (fp) {
std::cout << "Closing file\n";
fclose(fp);
}
}
};
void customDeleterDemo() {
std::unique_ptr<FILE, FileCloser> filePtr(fopen("test.txt", "w"));
if (filePtr) {
fputs("Hello, world!", filePtr.get());
}
} // filePtr销毁时自动调用FileCloser释放资源
int main() {
customDeleterDemo();
return 0;
}
enable_shared_from_this示例
#include <iostream>
#include <memory>
class Widget : public std::enable_shared_from_this<Widget> {
public:
std::shared_ptr<Widget> getPtr() {
return shared_from_this();
}
void say() { std::cout << "Widget here\n"; }
};
void sharedFromThisDemo() {
auto w = std::make_shared<Widget>();
auto w2 = w->getPtr();
w2->say();
std::cout << "Use count: " << w.use_count() << std::endl;
}
int main() {
sharedFromThisDemo();
return 0;
}
3. 常见错误使用及其后果
- • 裸指针与智能指针混用:导致重复释放或内存泄漏。
- • 复制
unique_ptr
:编译错误,必须用std::move
转移所有权。 - • 循环引用:
shared_ptr
互相持有,导致内存无法释放,必须用weak_ptr
打破。 - • 错误访问已过期的
weak_ptr
:未检测资源是否存在,导致悬空访问。 - • 用裸指针初始化多个
shared_ptr
:导致多重析构崩溃。 - • 过度使用
shared_ptr
:引用计数开销影响性能。
大项目中智能指针的实践策略
- • 优先使用
unique_ptr
管理独占资源,如工厂函数返回值、局部资源管理,性能最佳且语义明确。 - • 共享资源时使用
shared_ptr
,但需谨慎,避免滥用。 - • 设计双向关系或缓存时,用
weak_ptr
防止循环引用。 - • 接口设计时明确智能指针传递方式,避免不必要的引用计数增加。
- • 统一使用
std::make_unique
和std::make_shared
创建智能指针,提高异常安全和性能。 - • 代码审查重点关注智能指针用法,防止裸指针与智能指针混用。
总结
智能指针不仅是内存管理的工具,更是现代C++设计哲学的体现:自动化资源管理、明确所有权语义、提升异常安全性。它们让C++代码更健壮、更易维护,同时保留了高性能和灵活性。
真正掌握智能指针,意味着你能从繁琐且易错的手动内存管理中解放出来,把精力放在业务逻辑和算法创新上。智能指针是现代C++程序员不可或缺的武器,是写出高质量代码的关键。
本文首发于【讳疾忌医-note】公众号,未经授权,不得转载。
(加入我的知识星球,免费获取账号,解锁所有文章。)
阅读剩余
版权声明:
作者:讳疾忌医-note
链接:https://www.1217zy.vip/archives/777
文章版权归作者所有,未经允许请勿转载。
THE END