c++智能指针
介绍
为了更容易的使用动态内存,标准库提供了两种智能指针和一个伴随类。智能指针在使用完毕后会自动释放十分方便,但不规范而使用还是会造成错误。
shared_ptr
定义
智能指针也是模板需要我们提供额外的信息(指向的对象)。
shared_ptr<int> p1;//空指针,可以指向一个int
使用make_shared 函数
初始化shared_ptr最安全的方法是使用make_shared函数。
shared_ptr<int> p3=make_shared<int>(42)//p3指向一个值为42的int的shared_ptr
调用make_shared传递的参数必须与所用类型的某个构造函数相匹配,若我们没传参数,则会进行值初始化。
用另一个shared_ptr来初始化
shared_ptr<int> p4(p3);p4和p3指向相同的内存
使用new来初始化一个shared_ptr
shared_ptr<int> p5=shared_ptr<int>(new int(42));
值得注意的是shared_ptr的构造函数是explict的,我们无法隐式的要求一个shared_ptr指向new。
//这样是错误的!
shared_ptr<int> p5=new int(42);
用new初始化shared_ptr是不安全的,我们后面再来讨论。
使用
多个shared_ptr可以指向相同的内存,且当最后一个shared_ptr销毁时,指向的内存才会销魂。我们可以利用shared_ptr在多个对象间共享数据。
拷贝和赋值
当进行拷贝和赋值时,每个shared_ptr都会记录有多少个其他的shared_ptr指向相同的对象。
auto p=make_shared<int>(42);引用者只有一个
auto q(p);现在有两个
auto w=make_shared<int>(32);
q=w;32的引用者+1,42的引用者-1;当42的引用者为0时会自动释放。
自动销毁
我们知道当引用者为0时就会自动销毁。
void use_factory(T arg)
{
shared_ptr<Foo> p=factory(arg);//离开函数后p指向内存自动释放
}
shared_ptr是使用析构函数进行释放的。
shared_ptr易错点:
new和shared_ptr
当我们使用New来初始化shared_ptr时,很容易将同一块内存绑定在多个独立创建的shared_ptr上。
//下面代码只是例子,会造成double free
int *x(new int(1024));
shared_ptr<int> k(x);
shared_ptr<int> l(x);
cout<<k.use_count ()<<endl;
cout<<l.use_count()<<endl;
//k和l同时指向x内存但它们是独立创建的,也就是说它们的引用是各自计算的,同时它们会二次释放同一块内存,造成错误。
get
不要用get初始化另一个智能指针或为另一个智能指针赋值。get返回的是一个普通的指针。
shared_ptr<int> p(new int(42)>;
int *q=p.get();//q和p指向同一块内存.
{
shared_ptr<int> t(q);
}
//此时p,q指向内存被释放,任何解引用p,q操作都是未定义的,且在程序结束时会造成double free
unique_ptr
unique_ptr拥有它所指向的对象。当一个unique_ptr销毁时,它所指向的内存也会销毁。
初始化
unique_ptr不支持普通的拷贝和赋值操作
unique_ptr<string> p1((new) string("Stegosaurus"));
unique_ptr<string> p2(p1); //不支持拷贝!
unique_ptr<string> p3=p2; //不支持赋值!
改变unique_ptr的指向
虽然我们不能直接通过赋值来改变unique_Ptr的指向,但我们可一通过release和reset将指针的所有权转移。
u.release() //u放弃指针的所有权,返回指针,将u置空
u.reset() //释放U指向的对象
u.reset(q),如果提供了内置指针q,u就会指向这个对象
下面是例子
unique_ptr<string> p2(p1.release());//p1置空,p2指向p1
unique_ptr<string> p3(new string("Trex"));
p2.reset(p3.release());//释放p2所指内存,p2接管p3所指内存,并将p3置空
从函数返回unique_ptr
不能拷贝unique_ptr但是有个例外,我们可一返回或赋值一个将被销毁的unique_ptr。
unique_ptr<int> clone(int p)
{
return unique_ptr<int>(new int(p));
weak_ptr
weak_ptr是一个弱引用,它不会控制所指向对象的生存期,它指向一个shared_ptr所管理的对象.一旦最后一个shared_ptr被销毁,其所指的对象也会被销毁。
shared_ptr<int> p =make_shared<int>(42);
weak_ptr<int> wp(p);//p引用不会变
使用
当我们使用一个weak_ptr时,我们并不知道它所指的对象是否还存在。我们可以调用lock来返回它所指向的对象。
if (shared_ptr<int> np=wp.lock())//若wp所指对象还存在就会返回一个指向对象的shared_ptr指针,若不存在则会返回一个空的shared_ptr
{
//在此函数体内,可安全使用所指对象。
}