【C++11】智能指针
摘要:本文详细解析了C++智能指针的演进与实现。从C++98有缺陷的auto_ptr(存在拷贝导致源对象失效问题)到C++11三大智能指针:unique_ptr(独占所有权、禁止拷贝但支持移动)、shared_ptr(引用计数实现资源共享)和weak_ptr(解决循环引用问题)。重点剖析了它们的实现原理,包括RAII机制、控制块设计及引用计数管理,并提供了简化版代码实现,展示了如何通过移动语义、控
智能指针是现代C++的关键,它能帮助自动管理内存、避免内存泄漏、提升代码安全性和可维护性。在C++98中就提出过auto_ptr,但是实际应用是收到市场的广泛投诉,在项目中几乎被禁用,后面C++11引入三大核心智能指针:unique_ptr、shared_ptr和weak_ptr。
智能指针的核心思想是RAII(Resource Acquisition Is Initialization),即资源的生命周期由对象的生命周期管理——对象销毁时自动释放资源。指针指针不是指针,但是要能像指针一样操作。
一、auto_ptr
我们简单实现一下auto_ptr:
template<class T>
class auto_ptr
{
public:
auto_ptr()
:_ptr(nullptr)
{}
auto_ptr(T* p)
:_ptr(p)
{}
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
void operator=(auto_ptr<T>& ap)
{
if (_ptr != nullptr)
{
delete _ptr;
_ptr = nullptr;
}
swap(ap._ptr, _ptr);
}
T operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
~auto_ptr()
{
if (_ptr != nullptr)
{
std::cout << "~auto_ptr:" << this << std::endl;
delete _ptr;
_ptr = nullptr;
}
}
private:
T* _ptr;
};
从代码中不难看出auto_ptr是存在巨大问题,其就体现在拷贝构造和赋值拷贝都会导致被拷贝的智能指针置空,这一点就这违反了 C++ 的基本约定:拷贝操作应该"值语义"——拷贝后两个对象等价且独立。但 auto_ptr 的拷贝会让源对象失效,极易引发 bug。而进一步就不能用于STL容器,因为 STL 容器(如 vector)在内部会进行拷贝操作(例如扩容、排序),而 auto_ptr 的拷贝会清空原对象,导致容器中出现大量 nullptr!所以auto_ptr被抛弃了。
二、unique_ptr
这是一个简易版的unique_ptr:
template<class T>
class auto_ptr
{
public:
auto_ptr()
:_ptr(nullptr)
{}
auto_ptr(T* p)
:_ptr(p)
{}
auto_ptr(auto_ptr<T>& ap)
:_ptr(ap._ptr)
{
ap._ptr = nullptr;
}
void operator=(auto_ptr<T>& ap)
{
if (_ptr != nullptr)
{
delete _ptr;
_ptr = nullptr;
}
swap(ap._ptr, _ptr);
}
T operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
~auto_ptr()
{
if (_ptr != nullptr)
{
std::cout << "~auto_ptr:" << this << std::endl;
delete _ptr;
_ptr = nullptr;
}
}
private:
T* _ptr;
};
template<class T>
class unique_ptr
{
public:
unique_ptr()
:_ptr(nullptr)
{}
unique_ptr(const unique_ptr<T>& up) = delete;
unique_ptr(unique_ptr<T>&& up)
:_ptr(up._ptr)
{
up._ptr = nullptr;
}
unique_ptr(T* p)
:_ptr(p)
{}
~unique_ptr()
{
delete _ptr;
_ptr = nullptr;
}
void operator=(unique_ptr<T>& up) = delete;
unique_ptr& operator=(unique_ptr<T>&& up) noexcept
{
if (this != &up) { // 防止自移动(虽罕见,但安全)
delete _ptr; // 先释放当前资源
_ptr = up._ptr; // 接管新资源
up._ptr = nullptr; // 源对象置空
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
private:
T* _ptr;
};
}
从代码中可以看到,unique_ptr禁用了拷贝构造和拷贝赋值。
| 特性 | 说明 |
|---|---|
| ✅ 独占所有权 | 同一时间只有一个 unique_ptr 拥有该资源 |
| ✅ 自动释放 | 离开作用域时自动调用 delete(或自定义删除器) |
| ✅ 零开销 | 编译后性能 ≈ 原始指针(无引用计数) |
| ❌ 不可拷贝 | 禁止拷贝构造和拷贝赋值(防止意外共享) |
| ✅ 可移动 | 支持移动语义(std::move),安全转移所有权 |
| ✅ 异常安全 | 即使发生异常,也能保证资源释放 |
三、shared_ptr和weak_ptr
- 多个
shared_ptr可共享同一对象 - 内部维护引用计数
- 当最后一个
shared_ptr销毁时,自动释放资源
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr = nullptr)
:_ptr(ptr),
_pcounter(ptr ? new size_t(1) : nullptr)
{}
shared_ptr(shared_ptr<T>& sp)
:_ptr(sp._ptr)
,_pcounter(sp._pcounter)
{
++(*_pcounter);
}
shared_ptr(shared_ptr<T>&& sp) noexcept
: _ptr(sp._ptr), _pcounter(sp._pcounter)
{
sp._ptr = nullptr;
sp._pcounter = nullptr;
}
~shared_ptr()
{
if (_pcounter && --(*_pcounter) == 0) {
delete _ptr;
delete _pcounter;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T* get()
{
return _ptr;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (this != &sp)
{
if (sp._pcounter) ++(*sp._pcounter);
if (_pcounter && --(*_pcounter)==0)
{
delete _ptr;
delete _pcounter;
}
_ptr = sp._ptr;
_pcounter = sp._pcounter;
}
return *this;
}
shared_ptr<T>& operator=(shared_ptr<T>&& sp)
{
if (this != &sp)
{
if (_pcounter && --(*_pcounter) == 0)
{
delete _ptr;
delete _pcounter;
}
_ptr = sp._ptr;
_pcounter = sp._pcounter;
sp._ptr = nullptr;
sp._pcounter = nullptr;
}
return *this;
}
size_t use_count() const { return _pcounter ? *_pcounter : 0; }
private:
T* _ptr;
size_t* _pcounter;
};
但是shared_ptr还存在一个问题,即当存在循环引用的时候就会资源泄露,代码如下:
class Node {
public:
Node(int v = 0)
:val(v)
, next(nullptr)
, pre(nullptr)
{}
~Node()
{
std::cout << "~Node:" << this << std::endl;
}
int val;
pl::shared_ptr<Node> next;
pl::shared_ptr<Node> pre;
};
int main()
{
pl::shared_ptr<Node> sp1(new Node(1));
pl::shared_ptr<Node> sp2(new Node(2));
sp1->next = sp2;
sp2->pre = sp1;
return 0;
}
运行结果如下,发现并没有调用析构函数,即资源泄露了。

这是因为Node中还存在shared_ptr,导致引用计数器一直不能清零,导致资源泄露。
针对shared_ptr的循环引用导致的资源泄露就提出了weak_ptr。weak_ptr与shared_ptr的区别就是weak_ptr没有了引用计数器,即weak_ptr只有资源的使用权没有资源的管理权。
增添weak_ptr后,就不再使用_pcounter来记录引用数量,而是使用控制块:
template<class T>
struct ControlBlock {
ControlBlock(T* p):
ptr(p),
shared_count(1),
weak_count(0)
{}
T* ptr;
size_t shared_count;
size_t weak_count;
};
控制块原理如下:ptr指向数据,shared_count用于记录shared_ptr指向该数据的数量,weak_count用于记录weak_ptr指向该数据的数量。只有shared_ptr才有删除ptr的权限,即shared_count==0时,可以delete ptr;shared_ptr和weak_ptr都有权限删除控制块,即shared_count==0&&weak_count==0时,shared_ptr和weak_ptr都有权限删除控制块。
简易代码如下:
template<class T>
class shared_ptr {
public:
shared_ptr(T* p = nullptr) :_cb(p ? new ControlBlock<T>(p) : nullptr)
{}
shared_ptr(const shared_ptr<T>& sp)
{
if (sp._cb)
{
sp._cb->shared_count++;
}
_cb = sp._cb;
}
~shared_ptr()
{
if ( _cb && _cb->shared_count == 1)
{
_cb->shared_count--;
delete _cb->ptr;
if (_cb->weak_count == 0)
{
delete _cb;
}
}
}
T& operator*()
{
return *(_cb->ptr);
}
T* operator->()
{
return _cb->ptr;
}
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{
if (this != &sp)
{
if (sp)
{
sp._cb->shared_count++;
}
if (_cb && --(_cb->shared_count) == 0)
{
delete _cb->ptr;
if (_cb->weak_count == 0)
{
delete _cb;
}
}
_cb = sp._cb;
}
return *this;
}
ControlBlock<T>* get()
{
return _cb;
}
private:
ControlBlock<T>* _cb;
};
template<class T>
class weak_ptr {
public:
weak_ptr() :
_cb(nullptr)
{}
weak_ptr(shared_ptr<T>& sp) : _cb(sp.get())
{
if (sp.get())
{
_cb->weak_count++;
}
}
~weak_ptr() {
if (_cb && --(_cb->weak_count) == 0) {
if (_cb->weak_count == 0&&_cb->shared_count==0)
{
delete _cb;
}
}
}
shared_ptr<T> lock() const
{
if (_cb && _cb->shared_count > 0)
{
return shared_ptr<T>(_cb);
}
return shared_ptr<T>();
}
weak_ptr<T>& operator=(shared_ptr<T>& sp)
{
if (sp.get())
{
sp.get()->weak_count++;
}
if (_cb)
{
if (--(_cb->weak_count) == 0 && _cb->shared_count==0)
{
delete _cb;
}
}
_cb = sp.get();
return *this;
}
weak_ptr<T>& operator=(const weak_ptr<T>& sp)
{
if (this != &sp)
{
if (sp)
{
sp._cb->weak_count++;
}
if (_cb && --(_cb->weak_count) == 0)
{
if (_cb->shared_count == 0)
{
delete _cb;
}
}
_cb = sp._cb;
}
return *this;
}
private:
ControlBlock<T>* _cb;
};
更多推荐


所有评论(0)