智能指针利用了RAII思想,利用智能指针对象的生命周期控制资源的分配和释放,资源包括堆内存,打开的文件,socket等。
其中shared_ptr使用引用计数的方式,允许多个shared_ptr对象引用资源,每有一个对象引用资源引用计数加一,当引用计数为0时,释放管理的资源。
unique_ptr只允许一个unique_ptr对象引用资源,支支持移动语义,不支持拷贝语义,移动语义下转移资源所有权,使用reset(release())方法。
但是shared_ptr存在循环引用问题。如双向链表的节点,有前驱和后继成员,都是shared_ptr,相邻节点互相引用,造成节点的引用计数最少是2,所引用的资源无法释放而产生泄露。
struct Node
{
int val;
shared_ptr<Node> pre;
shared_ptr<Node> next;
Node(int v) :val(v){}
};
/*OR
class B;
class A{
shared_ptr<B> ptr;
};
class B{
shared_ptr<A> ptr;
};
也可以造成循环引用
*/
int main()
{
shared_ptr<Node> p1(new Node(1)), p2(new Node(2));
p1->next = p2;
p2->pre = p1;
w1 = p1;
w2 = p2;
cout << p1.use_count() << " " << p2.use_count() << endl;//2 2
}
weak_ptr作为shared_ptr的补充,无法直接引用资源,必须靠shared_ptr初始化,不影响引用计数值,不负责资源的释放。所以在资源类中将其中一个成员的类型换成weak_ptr就能打破引用环(不用都换)。
struct Node
{
int val;
weak_ptr<Node> pre;
shared_ptr<Node> next;
Node(int v) :val(v){}
};
/*OR
class B;
class A{
weak_ptr<B> ptr;
};
class B{
shared_ptr<A> ptr;
};
*/
提供share_ptr<T> p = w.lock();
方法,当引用计数为0时返回的shared是nullptr,否则指向资源,从而通过shared完成对资源的释放。
weak_ptr<int> w(p);
if(w.use_count() != 0)//不对
{//当多线程操作时,判断完后,另一线程的shared释放了对象,当前线程再操作对象就会出错。
}
if(shared_ptr<int> q = w.lock())//对
{//即使另一线程释放了对象那么得到的q就是空的。
}
简化版
shared_ptr初始化两种方式:
- 传指针
int * a = new int(1);
shared_ptr<int> p(a), q(a);//这时两个智能指针对象p,q里的计数无法同步,都是1.
//避免这种
//尽量使用
shared_ptr<int> p(new int(1));
- make_shared
shared_ptr<int> p = make_shared<int>(1);
template <typename T>
class shared_ptr
{
private:
T* data_ptr;
size_t* count_ptr;
void minusone()
{//引用计数减一
if (--*count_ptr == 0)
{
delete data_ptr;
delete count_ptr;
data_ptr = count_ptr = nullptr;
}
}
public:
shared_ptr() :data_ptr(nullptr),
count_ptr(new size_t(0)) {}
explicit shared_ptr(T* p): data_ptr(p),
count_ptr(new size_t(1)){}
shared_ptr(const shared_ptr& other) : data_ptr(other.data_ptr),
count_ptr(&(++*other.count_ptr)) {}
~shared_ptr()
{
minusone();
}
shared_ptr& operator=(const shared_ptr& other)
{
if (*this == other)
return *this;
minusone();
++*other.count_ptr;
data_ptr = other.data_ptr;
count_ptr = other.count_ptr;
return *this;
}
void swap(const shared_ptr& other)
{
using std::swap;
if (*this == other)
return *this;
swap(other.count_ptr, count_ptr);
swap(other.data_ptr, data_ptr);
}
void reset(T* p)
{
minusone();
data_ptr = p;
*count_ptr = 1;
}
T* release()
{
minusone();
return data_ptr;
}
};
添加删除函数
template <typename T>
class shared_ptr
{
typedef void(*DelFuncPtr)(T*);
private:
T* data_ptr;
size_t* count_ptr;
DelFuncPtr del;
void minusone()
{
if (--*count_ptr == 0)
{
del(data_ptr);
delete count_ptr;
data_ptr = nullptr;
count_ptr = nullptr;
}
}
public:
shared_ptr() :data_ptr(nullptr), count_ptr(new size_t(0)),
del(nullptr) {}
explicit shared_ptr(T* p, DelFuncPtr d = nullptr) : data_ptr(p),
count_ptr(new size_t(1)), del(d){}
shared_ptr(const shared_ptr& other) : data_ptr(other.data_ptr),
count_ptr(&(++*other.count_ptr)), del(other.del) {}
~shared_ptr()
{
del(data_ptr);
delete count_ptr;
}
shared_ptr& operator= (const shared_ptr& other)
{
if (other == *this)
return *this;
minusone();
++*other.count_ptr;//拷贝构造和赋值运算,一定不要忘了两个对象的引用计数都要加1.
data_ptr = other.data_ptr;
count_ptr = other.count_ptr;
del = other.del;
return *this;
}
void swap(shared_ptr& other)
{
swap(other.data_ptr, data_ptr);
swap(other.count_ptr, count_ptr);
swap(other.del, del);
}
void reset(T* p, DelFuncPtr d=nullptr)
{
//丢弃原本指向的对象,转而指向新的对象。
minusone();
data_ptr = p;
count_ptr = 1;
del = d;
}
T* release()
{
minusone();
return data_ptr;
}
};