相比前面的类型支持(Type Support)部分,这部分的内容编程里相对来说使用频率更高,比如智能指针。
1.Smart pointers 智能指针
包含4个内容,unique_ptr/shared_ptr/weak_ptr/auto_ptr,其中前三个是C++11后纳入标准库的,更早的在boost或者<tr1/memory>中也有支持。
1.1 shared_ptr<T>
应该算是最常用的智能指针了,几乎所有的实现都采用在堆(heap)上放个计数值(count)的办法,当引用计数为0时则销毁其管理的资源,基本使用方法这里不再做多的说明。下面主要用代码的方式做一些特别说明:
A *a = new A(1);
{
std::shared_ptr<A> p1(a);
std::shared_ptr<A> p2(a);
}
p1和p2的引用资源实际上是分开的!当程序离开{}块的时候,p1对象离开{}块后引用计数编程0,将指针a的资源释放;p2对象离开{}块后执行同样的操作,但是指针a已经被析构掉了!!指针a这样就被析构两次,程序出现堆错误。正确的用法是如下:std::shared_ptr<A> p1(new A(1));
std::cout << p1.use_count() << std::endl;</span>
{//这里只是为了测试引用计数加的大括号
std::shared_ptr<A> p2(p1);
std::cout << p1.use_count() << std::endl;
}
std::cout << p1.use_count() << std::endl;</span>
2)线程安全
shared_ptr引用计数本身是安全且无锁的,但对象的读写则不是,实际上 shared_ptr 有两个数据成员,一个用于保存指向实际对象的指针,另一个则是引用计数器。在进行shared_ptr赋值、拷贝构造、析构(写操作)的时候分了两步——先将指针指向实际对象,然后在引用计数器上+1,这样必然导致操作不是原子的。在多线程同时读写同一个智能指针对象时就必须加锁:
std::shared_ptr<A> p(new A);
std::shared_ptr p1;
// thread A
p1 = p; //读取p
// thread B
p.reset(); //如果线程A读取p的同时线程B对p进行了写操作,那么会导致未知行为。
类似的还有同时写的时候也要加锁:
std::shared_ptr<int> p;
// thread A
p.reset(new int(1));
// thread B
p.reset(new int(2)); //同时写入
或者:// thread A
p1 = p; //增加一个引用计数
// thread B
// p同时已经离开了作用域,其管理的资源已经被析构了
1.2 unique_ptr<T>
唯一指针,unique_ptr<T>的更换保管对象必须由std::move或者reset()成员函数去实现。相比原有的std::auto_ptr<T>不会莫名其妙的发现原有的保管对象被销毁的情况(更为安全)。相比于shared_ptr<T>,该指针可以支持动态数组。
文档里对几种典型应用作了描述,概括起来就是对“生命周期内要求被唯一指针指向”的对象进行管理。说的比较抽象,本人倒是经常在开辟动态数组的时候使用:
int size = 10;
std::unique_ptr<int[]> p1(new int[size]);
当p1离开作用域时,会自动调用delete[]对开辟的内存进行释放,这也从某方面贯彻了《Effective C++》里所讲的以对象管理内存资源的思想,省的new了之后忘记delete造成内存泄露。当然复杂点的场景也可以自己制定deleter。
1.3 weak_ptr<T>
A *a = new A(1);
{
std::shared_ptr<A> p1(a);
std::shared_ptr<A> p2(a);
}
由于p1销毁后p2不知道a的资源已经被释放了,造成再次访问出现问题,但是感觉正常来讲一般不会出现该情况。本人曾写过一个脱离框架的面向对象的定时器,由于Timer里面需要指定回调,为了解耦方便,本人用了std::function<void ()>去代替,但是如果指定了回调函数timeout_,那么假如指向的回调函数所在的对象被销毁了(而且函数内部使用的资源也被销毁了),那么当计时到了的时候调用timeout_就会出问题。为了解决该问题本人想了个偷懒的办法,既想很好的解耦又想方便代码的书写,于是就使用shared_ptr和weak_ptr,代码如下:class Timer
{
public:
void setTimeout(const std::shared_ptr<std::function<void ()> >& func)
{ timeout_ = func; }
const std::weak_ptr<std::function<void ()> >& timeout() const
{ return timeout_; }
private:
std::weak_ptr<std::function<void ()> > timeout_;
};
timeoutEvent(int timerfd)
{
...
//如果回调对象被删除了,根据weak_ptr的expired进行判断是否回调对象已经没了
if (!iter->second->timeout().expired())
{
(*iter->second->timeout().lock())();
}
...
}