智能指针的作用有如同指针,但会记录有多少个 shared_ptrs 共同指向一个对象。这便是所谓的引用计数。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为 0,这个对象会被自动删除。
shared_ptr 的实现机制其实就是在拷贝构造时使用同一份引用计数。
同一个 shared_ptr 被多个线程 “读” 是安全的,同一个 shared_ptr 被多个线程 “写” 是不安全的。
分析
比如说,用智能指针来创建一个动态分配的字符串对象:
// 新创建一个对象,引用计数器为 1
shared_ptr<string> pstr(new string("abc"));
解引用一个智能指针返回它指向的对象。同样,我们可以像操作普通指针一样调用 string 提供的方法。
if (pstr && pstr->empty()) {
*pstr = "hello";
}
当有另外一个智能指针对当前智能指针进行拷贝时,引用计数器加 1:
shared_ptr<string> pstr(new string("abc")); // pstr 指向的对象只有一个引用者
shared_ptr<string> pstr2(pstr); // pstr 跟 pstr2 指向相同的对象,此对象有两个引用者
当两个智能指针进行赋值操作时,左边的指针指向的对象引用计数减 1,右边的加 1。
shared_ptr<string> pstr(new string("abc"));
shared_ptr<string> pstr2(new string("hello"));
// 给 pstr2 赋值,令他指向另一个地址,递增 pstr 指向的对象的引用计数,递减 pstr2 原来指向的对象引用计数
pstr2 = pstr;
指针离开作用域范围时,同样引用计数减1。当引用计数为0时,对象被回收。
简单实现
template <typename T>
class smart_ptrs
{
public:
smart_ptrs(T*); // 用普通指针初始化智能指针
smart_ptrs(smart_ptrs&);
T* operator->(); // 自定义指针运算符
T& operator*(); // 自定义解引用运算符
smart_ptrs& operator=(smart_ptrs&); // 自定义赋值运算符
~smart_ptrs(); // 自定义析构函数
private:
int *count; // 引用计数
T *p; // 智能指针底层保管的指针
};
跟标准库一样,我们使用模板来实现它。
用普通指针进行初始化时,需要将该指针进行封装,并且引用计数初始化为1。
template <typename T>
smart_ptrs<T>::smart_ptrs(T *p): count(new int(1)), p(p) {
}
定义拷贝构造函数:
template <typename T>
// 对普通指针进行拷贝,同时引用计数器加 1,因为需要对参数进行修改,所以没有将参数声明为 const
smart_ptrs<T>::smart_ptrs(smart_ptrs &sp): count(&(++*sp.count)), p(sp.p) {
}
定义指针运算符:
template <typename T>
T* smart_ptrs<T>::operator->() {
return p;
}
定义解引用运算符,直接返回底层指针的引用:
template <typename T>
T& smart_ptrs<T>::operator*() {
return *p;
}
定义赋值运算符,左边的指针计数减 1,右边指针计数加 1,当左边指针计数为 0 时,释放内存:
template <typename T>
smart_ptrs<T>& smart_ptrs<T>::operator=(smart_ptrs& sp) {
++*sp.count;
if (--*count == 0) { // 自我赋值同样能保持正确
delete count;
delete p;
}
this->p = sp.p;
this->count = sp.count;
return *this;
}
定义析构函数:
template <typename T>
smart_ptrs<T>::~smart_ptrs() {
if (--*count == 0) {
delete count;
delete p;
}
}
大功告成!
后面是个小测试:
#include <iostream>
using namespace std;
template <typename T>
class smart_ptrs
{
public:
smart_ptrs(T*); // 用普通指针初始化智能指针
smart_ptrs(smart_ptrs&);
T* operator->(); // 自定义指针运算符
T& operator*(); // 自定义解引用运算符
smart_ptrs& operator=(smart_ptrs&); // 自定义赋值运算符
~smart_ptrs(); // 自定义析构函数
private:
int *count; // 引用计数
T *p; // 智能指针底层保管的指针
};
// 定义构造函数
template <typename T>
smart_ptrs<T>::smart_ptrs(T *p): count(new int(1)), p(p) {
}
// 定义拷贝构造函数
template <typename T>
// 对普通指针进行拷贝,同时引用计数器加 1,因为需要对参数进行修改,所以没有将参数声明为 const
smart_ptrs<T>::smart_ptrs(smart_ptrs &sp): count(&(++*sp.count)), p(sp.p) {
}
// 定义指针运算符
template <typename T>
T* smart_ptrs<T>::operator->() {
return p;
}
// 定义解引用运算符,直接返回底层指针的引用
template <typename T>
T& smart_ptrs<T>::operator*() {
return *p;
}
// 定义赋值运算符,左边的指针计数减 1,右边指针计数加 1,当左边指针计数为 0 时,释放内存
template <typename T>
smart_ptrs<T>& smart_ptrs<T>::operator=(smart_ptrs& sp) {
++*sp.count;
if (--*count == 0) { // 自我赋值同样能保持正确
delete count;
delete p;
}
this->p = sp.p;
this->count = sp.count;
return *this;
}
// 定义析构函数
template <typename T>
smart_ptrs<T>::~smart_ptrs() {
if (--*count == 0) {
delete count;
delete p;
}
}
// test
int main()
{
smart_ptrs<string> pstr(new string("abc"));
smart_ptrs<string> pstr2(pstr);
smart_ptrs<string> pstr3(new string("bcd"));
pstr3 = pstr2;
return 0;
}
直接用 gdb 看每一步的输出结果就 OK 了:
p *pstr.count
p *pstr.p
四句话的输出分别是:
1
"abc"
2
"abc"
2
"abc"
3
"abc"