<Effective Modern C++>Item 18: Use std::unique_ptr for exclusive-ownership resource management.

来源:互联网 发布:卖esse爱喜烟的淘宝店 编辑:程序博客网 时间:2024/05/22 10:56

特别提这一章是因为自己习惯于使用shared_ptr,而一直忽略unique_ptr的使用,甚至对exclusive-ownership的对象我也是一律采用shared_ptr的方案,而丝毫没有考虑unique_ptr的感受…所以必须总结一番unique_ptr的特性。

首先先简单回顾一番智能指针的发展历程…
在C++98之前,标准库中已经有了std::auto_ptr这样的智能指针,这可以看作是C++对RAII思想的一种尝试,所以auto_ptr的成熟度是不合格的。C++11带来了三种现代指针:shared_ptr / unique_ptr / weak_ptr,其中shared_ptrweak_ptr是配合使用的,而unique_ptr则完全替代了auto_ptr
所以除非是不得已要维护C++98之前的代码,那么请弃用auto_ptr

好的下面是我们今天的主角——unique_ptr
不仅unique_ptr的行为接近原生指针,它的内存占用也可以认为是和原生指针一样的(使用默认的deleter),可以说是非常轻量级了,因此大多场景下也应该是我们的优先考虑对象。
一个unique_ptr独占它所指向的实例,但更需要注意的是——unique_ptrmoveable but not copyable的。uncopyable很好理解,毕竟和unique对应上了,其拷贝构造函数和赋值操作符都通过=delete禁用了。但是moveable一开始却十分困惑我。然而存在及合理——一方面,我猜测是为了优化性能,其次是可以将源指针的管理权限通过move转移到目标指针,注意是转!移!而不是赋值/复制,但更重要的,moveable的特性为unique_ptr 存入容器和作为函数返回值 提供了可能。

重点说作为函数返回值。
unique_ptr作为函数返回值的一个典型应用就是工厂模式,实现如下:

template<class T,     typename... Types>unique_ptr<T> getInstance(Types&&... Args){    return (unique_ptr<T>(new T(std::forward<Types>(Args)...)));   }/* 既然用了C++14那么也可以更彻底一点template<class T,     typename... Types>auto getInstance(Types&&... Args){    return (unique_ptr<T>(new T(std::forward<Types>(Args)...)));   }*///通过如下调用使用auto new_instance=getInstance(params);

经过测试,在return语句中构造unique_ptr对象确实不会调用拷贝构造函数,查阅相关资料后发现:

当函数返回一个对象时,理论上会产生临时变量,那必然是会导致新对象的构造和旧对象的析构,这对效率是有影响的。C++编译针对这种情况允许进行优化,哪怕是构造函数有副作用,这叫做返回值优化(RVO),返回有名字的对象叫做具名返回值优化(NRVO).

酷的不行…

unique_ptrshared_ptr一样,有一个默认的删除器(直接使用delete删除对应指针),但是用户也可以根据所管理的对象构成设计自己的删除器,就像下面这样:

// C++11/14风格 + lambda表达式auto delInstance = [](SomeClass* pInstance){makeLogEntry(pInstance);// sth else.delete pInstance;};//在构造unique_ptr时传入删除器std::unique_ptr<SomeClass,decltype(delInstance)> pIns(nullptr,delInstance);

注:指定模板类型时使用decltype推导delInstance的类型,在构造实例时传入具体的delInstance方法作为删除器,书中更建议使用lambda来设计删除器,积极拥抱新标准。
(此时unique_ptr的大小可就和原生指针不一样了,具体大小取决于删除器这个function object是如何工作的,不合理的删除器设计往往会导致unique_ptr的大小膨胀)

unique_ptr是有两种形式的——单独的实体(std::unique_ptr<T>)和数组(std::unique_ptr<T[]>),具体如何使用取决于使用者,但要注意前者没有重载[],而后者没有重载* / ->

总之,对于exclusive-ownership的对象,应该尽量使用unique_ptr,这才是其实际意义所在,并且unique_ptrshared_ptr之前的转换也十分方便,甚至可以用直接前面的getInstance函数构造一个shared_ptr对象。

阅读全文
0 0