前言
智能指针是C++中用于自动化资源管理的对象,它封装了原始指针,并提供了自动内存管理的功能。智能指针的核心思想是RAII(Resource Acquisition Is Initialization),即在对象构造时获取资源,在对象析构时释放资源。
为什么需要智能指针
// 传统指针的问题示例
void problemDemo() {int* rawPtr = new int(10); // 分配内存if (someCondition) {throw std::runtime_error("Error!"); // 抛出异常// 下面的delete不会被执行,导致内存泄漏}delete rawPtr; // 手动释放内存
}
C++标准库中的智能指针
1. std::unique_ptr
template<class _Ty>
struct my_default_deleter
{void operator()(_Ty* ptr)const{delete ptr;}
};
template<class _Ty,class _Dx=my_default_deleter<_Ty>>
class my_unique_ptr
{
public:using element_type = _Ty;using pointer = _Ty*;using delete_type = _Dx;
private:_Ty* _mptr;delete_type mdeleter;
public:delete_type get_deleter()const { return mdeleter; }operator bool()const { return _mptr != nullptr; }my_unique_ptr(_Ty*p=nullptr):_mptr(p){}void swap(my_unique_ptr& _X){std::swap(this->_mptr, _X._mptr); // 添加std::}pointer release() // {pointer old = _mptr;_mptr = nullptr;return old;}void reset(pointer ptr = nullptr){mdeleter(_mptr); // 保持原调用_mptr = ptr;}pointer get()const{return _mptr;}element_type& operator*()const {return *get();}pointer operator->()const { return get(); }my_unique_ptr(const my_unique_ptr&) = delete;~my_unique_ptr(){reset(nullptr);}my_unique_ptr(my_unique_ptr&&other) :_mptr(other._mptr){other._mptr = nullptr;}my_unique_ptr& operator=(const my_unique_ptr&) = delete;// 修正移动赋值运算符 (移除const)my_unique_ptr& operator=(my_unique_ptr&& other) // 关键修改{if (this != &other) {reset(); // 使用reset释放资源_mptr = other._mptr;other._mptr = nullptr;}return *this;}
};
默认删除器模板 `my_default_deleter`:这是一个简单的仿函数,用于删除由`new`分配的单个对象, 它有一个成员函数 `operator()`,接受一个`_Ty*`类型的指针,然后使用`delete ptr;`释放该指针指向的对象。
`my_unique_ptr` 类模板:有两个模板参数:`_Ty`(指向的对象类型)和`_Dx`(删除器类型,默认为`my_default_deleter<_Ty>`)
element_type`: 指向的元素类型,即`_Ty`,`pointer`: 指针类型,即`_Ty*`,delete_type`: 删除器类型,即`_Dx
构造函数 my_unique_ptr(_Ty* p = nullptr)
参数 p
:要托管的原始指针(默认为空指针)
从原始指针创建智能指针
reset(pointer ptr = nullptr)
释放当前托管对象(如果有),然后接管新对象执行流程:设置 _mptr = ptr
- 接管新指针
调用 mdeleter(_mptr)
- 使用删除器释放当前资源release()
放弃所有权但不释放资源返回原始指针,调用者负责管理内部指针被置空
移动构造函数 my_unique_ptr(my_unique_ptr&& other)
从右值(临时对象)转移所有权复制指针后,将源对象置空源对象变为空指针状态
移动赋值运算符 operator=(my_unique_ptr&& other)
从右值转移所有权处理流程:将源对象置空接管源对象的指针reset()
释放当前资源检查自赋值
析构函数 ~my_unique_ptr()
自动调用 reset(nullptr)
释放资源保证资源不会泄漏
unique_ptr是唯一性智能指针所以没有拷贝构造和移动拷贝构造
基本用法
#include <memory>void uniquePtrDemo() {// 创建unique_ptrstd::unique_ptr<int> ptr1(new int(5));// C++14后推荐使用make_uniqueauto ptr2 = std::make_unique<int>(10);// 移动语义转移所有权std::unique_ptr<int> ptr3 = std::move(ptr1);// ptr1现在为空if (!ptr1) {std::cout << "ptr1 is empty now\n";}// 自动释放内存(离开作用域时)
}
特点
-
独占所有权,不能拷贝 零额外开销(和裸指针大小相同)支持自定义删除器
2.2 shared_ptr(共享指针)
基本用法
void sharedPtrDemo() {// 创建shared_ptr(推荐make_shared)auto ptr1 = std::make_shared<int>(20);{// 共享所有权auto ptr2 = ptr1; std::cout << "Use count: " << ptr2.use_count() << "\n"; // 2} // ptr2析构,引用计数减1std::cout << "Use count: " << ptr1.use_count() << "\n"; // 1// 自动释放内存(引用计数为0时)
}
timelinetitle shared_ptr 生命周期section 创建ptr1 : 对象创建(计数=1)section 共享ptr2创建 : 计数=2section 部分释放ptr2销毁 : 计数=1(对象保留)section 完全释放ptr1销毁 : 计数=0(对象释放)
2.3 weak_ptr(弱指针)
基本用法
void weakPtrDemo() {auto shared = std::make_shared<int>(30);std::weak_ptr<int> weak = shared;// 使用前需要转换为shared_ptrif (auto temp = weak.lock()) {std::cout << "Value: " << *temp << "\n";} else {std::cout << "Object already destroyed\n";}
}
make_shared和直接构造的区别
// 方式1:两次内存分配(对象+控制块)
std::shared_ptr<Widget> p1(new Widget);// 方式2:一次内存分配(推荐)
auto p2 = std::make_shared<Widget>();
-
优先使用make_unique/make_shared 更高效 更安全 更简洁
-
不要滥用shared_ptr:默认使用unique_ptr 只在确实需要共享所有权时使用shared_ptr shared_ptr有额外开销(控制块、原子操作)
总结
记住智能指针的黄金法则:让资源的所有权清晰明确,让资源的释放自动发生。合理使用智能指针可以显著提高C++代码的安全性和可维护性。