shared_ptr使用场景
shared_ptr<int> create0(int value)
{
return make_shared<int>(value);
}
shared_ptr<int> myfunc(int value)
{
shared_ptr<int> tmp = create0(value);
return tmp;
}
int main()
{
myfunc(11); //如果这块我们不用share_ptr变量来接受myfunc返回的结果,那么从myfunc
//返回的shared_ptr就会被销毁,所指向的对象也会被销毁。
auto p11 = myfunc(12); //我们用了一个变量来接受myfunc返回值,那么myfunc返回的
//sahred_ptr就不会被销毁,所指向的对象也不会被销毁。
}
shared_ptr使用陷阱分析:一旦用错也是致命的
1、慎用裸指针
void proc(shared_ptr<int> ptr)
{
return;
}
int main()
{
int *p = new int(100);
proc(p); //语法错误,int *p不能转换成shared_ptr<int>(不支持隐式转换)
proc(shared_ptr<int>(p)); //参数是一个临时的shared_ptr,用一个裸指针显示构造
//此时从proc内部出来后,shared_ptr强引用计数变为0,所指向的内存被释放,因为p和
//shared_ptr所指向的内存是同一块内存所以shared_ptr释放后,p也不能再使用。
*p = 45; //潜在的不可预料的问题,因为p已经被释放了
}
int main()
{
int *p = new int(100);
shared_ptr<int> p2(p);
proc(p2); //这里proc将创建一个p2的临时对象副本去执行函数
*p = 45; //此时可以
}
把一个普通裸指针绑定到shared_ptr上之后,内存管理的责任就交给了,shared_ptr了,这个时候你就不应该在用裸指针来访问shared_ptr所指向的内存了。推荐下面的写法:
shared_ptr<int> myp(new int(100));
proc(myp);
*myp = 34;
一定不要用裸指针初始化多个shared_ptr
int *p = new int(12);
shared_ptr<int> p1(p);
shared_ptr<int> p2(p); //不可以!!!!这种写法p1和p2无关联,会导致p1和p2所指向的
//内存被释放两次,产生异常
shared_ptr<int> p1(new int());
shared_ptr<int> p2(p1); //这种写法可以,p1与p2用的是同一个控制块,两者互通
2、慎用get()返回的指针:返回智能指针对应的裸指针
有些函数接口可能只能使用裸指针。
get返回的指针不能delete,否则会异常
shared_ptr<int> myp(new int(122));
int *p = myp.get();
delete p; //不可以释放!!!!!!!!!!!!!!!
shared_ptr<int> myp2(p); //不可以!!!myp和myp2的引用计数都为1
不能将其他智能指针绑定到get返回的裸指针上
永远不要用get得到的指针来初始化,或者给另外一个智能指针赋值。
class CT
{
public:
shared_ptr<CT> getself()
{
return shared_ptr<CT>(this); //不要用裸指针生成智能指针
}
};
shared_ptr<CT> pct1(new CT());
shared_ptr<CT> pct2 = pct1->getself(); //不可以pct1与pct2未指向同一块内存
enable_shared_from_this<>
工作原理:enable_shared_from_this有一个弱指针,这个弱指针能够监视this在我们调用shared_from_this()这个方法时,这个方法内部实际上是调用了这个weak_ptr的lock()方法。lock()方法会让share_ptr引用计数加1,同时返回这个shared_ptr
class CT: enable_shared_from_this<CT>
{
public:
shared_ptr<CT> getself()
{
return shared_from_this(); //这个就是enable_shared_from_this类中的方法,要
//通过此方法返回智能指针
}
};
shared_ptr<CT> pct1(new CT());
shared_ptr<CT> pct2 = pct1->getself();
现在,在外面创建的CT对象的智能指针以及通过CT对象返回的this智能指针都是安全的
3、避免循环引用:
class CB;
class CA
{
public:
shared_ptr<CB> m_pbs;
~CA()
{
}
};
class CB
{
public:
shared_ptr<CA> m_pas;
//weak_ptr<CA> m_pas; 变成弱引用即可
~CB()
{
}
};
int main()
{
shared_ptr<CA> pca(new CA);
shared_ptr<CB> pcb(new CB);
pca->m_pbs = pcb; //等价于指向CB对象的有两个强引用
pcb->m_pas = pca; //等价于指向CA对象的有两个强引用
//结束后会导致无法释放,除非把其中一个的智能指针变成弱引用
}
性能说明
尺寸问题:
sahred_ptr的尺寸是裸指针的2倍。两个裸指针
weak_ptr的尺寸也是裸指针的2倍。两个裸指针
第一个裸指针指向的是这个智能指针所指向的对象
第二个裸指针 指向一个很大的数据结构(控制块),这个控制块里边有:所指对象的强引用计数;所指对象的弱引用计数;其他数据,比如删除器,内存分配器;
控制块是由第一个指向该对象的shared_ptr 来创建;
控制块创建时机:
1、make_shared:分配并初始化一个对象,返回指向此对象的shared_ptr,所以,这个make_shared他总是能够创建控制块。
2、用裸指针来创建一个shared_ptr对象时。
移动语义:
shared_ptr<int> p1(new int(100));
shared_ptr<int> p2(std::move(p1)); //移动语义,移动构造一个新的智能指针引用对象p2,
//移动后p1就不在指向该对象(变成空),引用计数依
//旧是1
shared_ptr<int> p3;
p3 = std::move(p2); //移动赋值,p2指向空,p3指向该对象,整个对象的引用计数仍然为1
移动肯定比复制快,复制要增加引用计数,移动不需要;
移动构造函数快过复制构造函数,移动赋值运算符快过拷贝赋值运算符
补充说明和使用建议:
shared_ptr:
1、分配器,解决内存分配问题 shared_ptr<int> p((new int), mydelete(), mymallocator<int>())
2、谨慎使用,奇怪用法不要轻易尝试。
3、优先使用make_shared()
shared_ptr<string> ps1(new string("good luck")); //按资料来说需要分配两次内存
auto ps2 = make_shared<string>("good luck"); //只分配一次