shared_ptr
shared_ptr 使用计数机制来表明资源被几个指针共享。可以通过成员函数use_count()来查看资源的所有者个数。除了可以通过new来构造,还可以通过传入auto_ptr, unique_ptr,weak_ptr来构造。当我们调用release()时,当前指针会释放资源所有权,引用计数减一。当引用计数等于0时,资源会被释放。
shared_ptr和unique_ptr都支持的操作:
- //空智能指针
shared_ptr<T> sp;
unique_ptr up;- p //将p作为一个条件判断,如果p指向一个对象,则为true
- *p //解引用p,获得它指向的对象。
- p->mem //等价于(*p).mem
- p.get(); // 返回p中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了。
- swap(p,q); 、p.swap(q); //交换p和q中的指针。
shared_ptr独有的操作:
- make_shared<T>(args);// 返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象。
- shared_ptr<T>p(q); // p是shared_ptr的拷贝;此操作会递增q中的计数器。q中的指针必须能转换成T*
- p = q;// p和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放。
- p.unique();// 若p.use_count()为1,返回true;否则返回false。
- p.use_count(); // 返回与p共享对象的只能指针数量;可能很慢,主要用于调试。
定义和改变shared_ptr的方法
- shared_ptr<T> p(q); //p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型。
- shared_ptr<T>p(u); //p从unique_ptr u那里接管了对象的所有权;将u置为空。
- shared_ptr<T>p(q, d);// p接管了内置指针q所指向的对象的所有权。q必须能够转换为T*类型。p将使用可调用对象d来代替delete。
- shared_ptr<T>p(p2, d);// p是shared_ptr p2的拷贝,唯一的区别是p将用可调用对象d来代替delete。
- p.reset(); // 若p是唯一指向其对象的shared_ptr,reset会释放此对象。–同unique_ptr。
- p.reset(q); //若传递了可选了参数内置指针q,会令p指向q,否则会将p置为空。–同unique_ptr。
- p.reset(q, d); //若还传递了参数d,将会调用d而不是delete来释放q。
错误的用法:
把指针单独定义出来,用同一个指针构造多个智能指针,会造成当一个智能指针析构时,另一个智能指针管理的内部指针被释放,程序崩溃。
void foo()
{
int *p = new int(1024);//单独定义指针p。
shared_ptr<int>ptr(p);//用指针p构造一个shared_ptr。
{
shared_ptr<int>ptr2(p);//用同一个指针构造另一个shared_ptr。此时,ptr2的引用计数是1,当ptr2出作用域的时候p被释放,程序crash。
}
}
//安全的写法
void foo()
{
shared_ptr<int>ptr(new int(1024));//构造shared_ptr的时候直接new一个指针。
{
shared_ptr<int>ptr2(new int(1024));
}
}
循环引用问题及解决方案
循环引用会导致指针无法析构,内存溢出。
类A引用了类B,类B也同样引用了类A,每个指针的引用计数都是2,为什么是2?
分析class A对象的引用情况,该对象被circleTes()函数中的a和class B对象中的_pb管理,因此A object引用计数是2,B object同理。
在这种情况下,当出作用域的时候,a和b的析构函数被调用,但是class A对象和class B对象仍然被一个智能指针管理,A object和B object引用计数变成1,于是这两个对象的内存无法被释放,造成内存泄露。
class A
{
public:
A(){};
~A(){};
void set(shared_ptr<B>pb){ _pb = pb;};
private:
shared_ptr<B>_pb;
};
class B
{
public:
B(){};
~B(){};
void set(shared_ptr<A>pa){ _pa= pa;};
private:
shared_ptr<A>_pa;
};
void circleTest()
{
shared_ptr<A> a(new A);
shared_ptr<B> b(new B);
a->set(b);
b->set(a);
cout<<"a count="<<a.use_count()<<"b count="<<b.use_count(); // count 均为2.所以离开作用域时,引用计数不能减为0,a和b都不会自动析构。
}
解决方法
把class A或者class B中的shared_ptr改成weak_ptr即可,由于weak_ptr不会增加shared_ptr的引用计数,所以A object和B object中有一个的引用计数为1,在a和b析构时,会正确地释放掉内存。
make_shared函数
返回一个shared_ptr智能指针。
最安全的分配和使用动态内存的方法就是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。头文件和share_ptr相同,在memory中
必须指定想要创建对象的类型,定义格式见下面例子:
shared_ptr<int> p3 = make_shared<int>(42);
shared_ptr<string> p4 = make_shared<string>(10,'9');
shared_ptr<int> p5 = make_shared<int>();