- demo:
#include<memory>
#include<windows.h>
const wchar_t* const FILE_PATH = LR"(D:\temp\test.txt)";
#pragma optimize("",off)
struct HANDLEDeleter
{
void operator()(HANDLE hHandle)const {
if (hHandle != INVALID_HANDLE_VALUE && hHandle != NULL) {
CloseHandle(hHandle);
}
}
};
using HANDL_UNIQUE_PTR = std::unique_ptr<void, HANDLEDeleter>;
int main()
{
std::unique_ptr<HANDLE, HANDLEDeleter> hFile((void**) CreateFileW(FILE_PATH, GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, OPEN_ALWAYS, NULL));
return 0;
}
整体的阅读思路,跟着effective modern c++ 走
条款18: 使用std::unique_ptr 管理具备专属所有权的资源
- 阅读疑问1.
默认情况下,std::unique_ptr 和裸指针有着相同的尺寸,并且对于大多数的操作,它们都是精确执行了相同的指令。
std::unique_ptr的定义为:
// CLASS TEMPLATE unique_ptr SCALAR unique_ptr 模板类的定义,第二个参数没有??????有默认参数???,参见上面的模板参数
template <class _Ty, class _Dx /* = default_delete<_Ty> */>
class unique_ptr { // non-copyable pointer to an object
public:
using pointer = typename _Get_deleter_pointer_type<_Ty, remove_reference_t<_Dx>>::type;
using element_type = _Ty;
using deleter_type = _Dx;
template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>
constexpr unique_ptr() noexcept : _Mypair(_Zero_then_variadic_args_t()) {}
template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>
constexpr unique_ptr(nullptr_t) noexcept : _Mypair(_Zero_then_variadic_args_t()) {}
unique_ptr& operator=(nullptr_t) noexcept {
reset();
return *this;
}
template <class _Dx2 = _Dx, _Unique_ptr_enable_default_t<_Dx2> = 0>
explicit unique_ptr(pointer _Ptr) noexcept : _Mypair(_Zero_then_variadic_args_t(), _Ptr) {}
template <class _Dx2 = _Dx, enable_if_t<is_constructible_v<_Dx2, const _Dx2&>, int> = 0>
unique_ptr(pointer _Ptr, const _Dx& _Dt) noexcept : _Mypair(_One_then_variadic_args_t(), _Dt, _Ptr) {}
//.... 中间的一些函数声明
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
private:
template <class, class>
friend class unique_ptr;
_Compressed_pair<_Dx, pointer> _Mypair;// 唯一的成员
};
我们可以观察到,std::unique_ptr 只有一个成员,而这个成员的类型_Compressed_pair 是个模板类,其第一个参数为deletor ,第二个参数为所管理的资源的类型
而上一节中,我们可以看到
emplate <class _Ty1, class _Ty2, bool = is_empty_v<_Ty1> && !is_final_v<_Ty1>>
class _Compressed_pair final : private _Ty1 { // store a pair of values, deriving from empty first,之所以这么搞,是因为如果单独将一个空的对象存储为值,将至少占1 字节空间,派生了,就没关系了
public:
_Ty2 _Myval2;
using _Mybase = _Ty1; // for visualization
template <class... _Other2>
constexpr explicit _Compressed_pair(_Zero_then_variadic_args_t, _Other2&&... _Val2) noexcept(
conjunction_v<is_nothrow_default_constructible<_Ty1>, is_nothrow_constructible<_Ty2, _Other2...>>)
: _Ty1(), _Myval2(_STD forward<_Other2>(_Val2)...) {}
template <class _Other1, class... _Other2>
constexpr _Compressed_pair(_One_then_variadic_args_t, _Other1&& _Val1, _Other2&&... _Val2) noexcept(
conjunction_v<is_nothrow_constructible<_Ty1, _Other1>, is_nothrow_constructible<_Ty2, _Other2...>>)
: _Ty1(_STD forward<_Other1>(_Val1)), _Myval2(_STD forward<_Other2>(_Val2)...) {}
constexpr _Ty1& _Get_first() noexcept {
return *this;
}
constexpr const _Ty1& _Get_first() const noexcept {
return *this;
}
};
_compressed_pair 类继承了_Ty1,而其,当这个delete 操作仅仅是个动作而没有一些成员信息的时候,其大小为0,_compressed_pair 就如其类名一样,压缩了存储“对象”+“删除动作”,时所需要的存储两个对象的未知。那可能这里有一个疑问,哪里存储了这样的一个delete 操作呢?答案就是:该std::unique_ptr 的类型,这样的一个编译时信息,提供了其delete 操作应该做的动作
- 疑问2,对接口的自动析构,p119 ,ln2 指出,自定义析构器现身以后,情况有所不同,若析构器是函数指针,那么std::unique_ptr 的尺寸一般会增加一到两个字长(word)。若析构器是函数对象,则带来的尺寸变化取决于该函数对象中存储了多少状态。无状态的函数对象(例如,无捕获的lambda 表达式)不会浪费任何存储尺寸,这意味着当一个自定义析构器既可以用函数,又可以用无捕获的lambda 表达式来实现,lambda 表达式是更好的选择。
-
首先,std::function 对象最少40 字节,这个已经讨论过了(https://blog.csdn.net/qq_18218335/article/details/105498941),且其调用是通过了虚函数,速度会慢一点点
-
如果是函数指针,那必须有地方存储这样的一个函数地址,因此,也需要增加额外的大小,
-
如果是lambda ,其本质是一个编译器生成的类,因此,如果没有captre/context的话,继承其,就相当于上面的父类大小为0,结果是省内存(其调用逻辑,用编译时的类型信息表达出来了,这里我们应该可以很强烈的体会到c++ 种类型信息所起到的强大作用
using handleDeleter = [](HANDLE hHandle) {
if (hHandle != INVALID_HANDLE_VALUE && hHandle != NULL) {
CloseHandle(hHandle);
}
};