[C++11]智能指针
C++11包括了三种智能指针:
- shared_ptr
- weak_ptr
- unique_ptr
shared_ptr
shared_ptr是一个包装类,内部包装了真正的数据指针以及引用计数,当引用计数为0时自动释放指针,跟指针一样,重载了->操作符,也可以用*来进行解引用。
简单介绍
#include <iostream>
#include <memory>
using namespace std;
int main(int /argc/, char const */argv/[])
{
int *p = new int(2);
/// 智能指针初始化/
shared_ptr<int> sp1(p);
/// 不能构造指向相同数据的智能指针,否则p会被释放两次/
/// shared_ptr<int> sp2(p);/
shared_ptr<int> sp2 = sp1;
shared_ptr<int> sp3 = sp1;
/// 三者的引用计数均为3/
cout << sp1.use_count() << endl;
cout << sp2.use_count() << endl;
cout << sp3.use_count() << endl;
/// 不声明指针的智能指针构造方法/
shared_ptr<int> sp4 = make_shared<int>(2);
shared_ptr<int> sp5 = sp4;
/// 引用计数为2/
cout << sp4.use_count() << endl;
cout << sp5.use_count() << endl;
/// 重置sp4,引用计数变为1/
sp4.reset();
cout << sp4.use_count() << endl; /// 0/
cout << sp5.use_count() << endl; /// 1/
/// 重置sp4,sp4和sp5指向不同的数据/
sp4.reset(new int(3));
cout << sp4.use_count() << endl; /// 1/
cout << sp5.use_count() << endl; /// 1/
/// 判断智能指针是否为空/解引用智能指针/
shared_ptr<int> sp;
if(!sp){
cout << "sp is null" << endl;
}
sp.reset(new int(5));
if(sp){
cout << "sp is " << *sp << endl;
}
return 0;
}
shared_ptr
构造类对象的智能指针
#include <iostream>
#include <memory>
using namespace std;
class Foo{
public:
Foo(int /i/, int /j/){
this->i = i;
this->j = j;
}
void print(){cout << i << j << endl;}
~Foo(){
cout << "destruct" << endl;
}
private:
int i;
int j;
};
int main(int /argc/, char const */argv/[])
{
shared_ptr<Foo> foo = make_shared<Foo>(2,3);
shared_ptr<Foo> foo2 = foo;
shared_ptr<Foo> foo3 = foo2;
cout << foo.use_count() << endl; /// 3/
Foo* foo4 = foo.get(); /// 不会改变引用的计数/
cout << foo.use_count() << endl; /// 3/
foo4->print();
foo2.reset();
foo3.reset();
foo.reset(); /// 调用析构/
/// 如果注释掉foo的重置,析构函数会发生在打印“end of main"以后/
cout << "End of main" << endl;
return 0;
}
weak_ptr
简单来说,weak_ptr就是不能增加引用计数的智能指针,也没有重载*和->等符号的智能指针,但是weak_ptr::use_count()可以查看引用计数,也可以通过weak_ptr::expired
查看是否过期。
#include <iostream>
#include <memory>
using namespace std;
int main(int /argc/, char const */argv/[])
{
weak_ptr<int> wp0;
cout << "wp0.expired() == " << std::boolalpha << wp0.expired() << endl; /// true/
shared_ptr<int> sp1(new int(5));
weak_ptr<int> wp1(sp1);
cout << "wp1.expired() == " << std::boolalpha << wp1.expired() << endl; /// false/
cout << "use count " << wp1.use_count() << endl; /// 1/
return 0;
}
同时,weak_ptr还能通过lock方法来获取一个shared_ptr智能指针,在获取shared_ptr智能指针的时候会提升智能指针的引用计数
shared_ptr<int> sp2(new int(4));
weak_ptr<int> wp2(sp2);
cout << "wp2.expired() == " << std::boolalpha << wp2.expired() << endl; /// false/
cout << "use count " << wp2.use_count() << endl; /// 1/
cout << "*wp2.lock() == "
<< *wp2.lock() << endl; /// 4/
cout << "use count " << wp2.use_count() << endl; /// 1/
shared_ptr<int> sp3(wp2);
shared_ptr<int> sp4(wp2);
/// 智能指针可以初始化多个shared_ptr,而普通指针不可以/
cout << "use count " << wp2.use_count() << endl; /// 3/
unique_ptr
顾名思义,unique_ptr只能有一个,不能复制,只能通过move实现转移内部指针。
unique_ptr<T>myPtr(newT);//ok
unique_ptr<T>otherPtr=myPtr;//编译错误
智能指针的应用场景
实现观察者模式
现在有一个设备模块,该模块从设备获取一条条数据(这里用Record表示一条数据),并通过观察者模式把Record分发给所有(观察了该数据的)观察者。观察者一般都是视图,如视图A得到数据后,通过表格显示数据的内容,而视图B上得到数据后,通过趋势图显示数据的内容。
当用户关闭了所有视图,意味没有视图再使用数据Record了,这时就可以释放掉Record;但是只要有一个视图在使用record,就不能释放Record。
如何管理Record的释放呢?这种情况使用智能指针,可以做到所有视图关闭后,自动释放Record。
工厂模式
假如你编写了一个工厂类,它提供一个接口,根据配置产生各种对象(内部调用new新建对象)。
由于该类很NewBee,它被封装为动态库提供给其他同事,当其他同事调用动态库得到新建对象后,新建对象将来由谁负责释放呢?如果没有统一且明确的沟通确认,很容易出现双方都忘记释放新建对象,或者同一个新建对象被双方都释放了一次的情况!
这时候你甩出了一个智能指针,然后宣布:大家都不用关心谁来释放了,让指针自己释放去吧。
::类对象应该让库释放还是让使用者释放一直很让人头疼::
避免代码发生异常时内存泄漏
void foo(){
try{
int *p = new int(0);
do_something(p);
delete p;
}
catch(...){
}
}
如果do_something函数中发生了异常,那么p就不会被释放,智能指针可以防止这种事情的发生