百科问答小站 logo
百科问答小站 font logo



unique_ptr 的底层实现是什么样的? 第1页

  

user avatar   qing-bo-72 网友的相关建议: 
      

底层一般是一个compressed_pair,运用c++中的空基类优化。

boost中有实现compressed_pair.hpp

微软stl中的实现compressed_pair

便于理解做简化

       template<typename T1,typename T2, bool = std::is_empty_v<T1>> struct compressed_pair : T1 {    T2  val2; }  template<typename T1,typename T2> struct compressed_pair<T1,T2,false> {    T1 val1;    T2 val2; };      

简单理解就是如果T1类型是空类的化,就继承它,利用空基类优化。否则就是个普通的pair。

这意味着如果你的删除器是无状态的话,unique_ptr尺寸就是一个指针大小。如果携带状态,就会导致尺寸变大。所以并不是定义删除器就会增大尺寸。


user avatar   zhang-yi-40-33-59 网友的相关建议: 
      

std::unique_ptr 需要在类内保存管理的对象指针和删除器对象,在 MSVC 的实现中,底层保存一个 _Compressed_pair,它能在删除器为空时应用空基类优化,以减小对象大小。这就是题主在不提供删除器时(使用默认的删除器 std::default_delete) 大小仅仅包含一个指针的大小的原因。

这里以我手头的 MSVC 的标准库实现为例:

_Compressed_pair 的实现如下:

       template <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 public:     _Ty2 _Myval2;     ... };  template <class _Ty1, class _Ty2> class _Compressed_pair<_Ty1, _Ty2, false> final { // store a pair of values, not deriving from first public:     _Ty1 _Myval1;     _Ty2 _Myval2;     ... };      

_Compressed_pair 的第三个模板参数的默认值用于确定该使用主模板还是其特化。通过判断第一个类型 _Ty1 是否为空(is_empty_v<_Ty1>)和是否可以继承(不为 final,即 !is_final_v<_Ty1>),选用两个特化。

其中,若 _Ty1 为空且可继承,则继承自 _Ty1,以实施空基类优化。我想注释中已经说的比较明确了。

为了构造 _Compressed_pair 对象,还定义了两个 tag:

       struct _Zero_then_variadic_args_t {     explicit _Zero_then_variadic_args_t() = default; }; // tag type for value-initializing first, constructing second from remaining args  struct _One_then_variadic_args_t {     explicit _One_then_variadic_args_t() = default; }; // tag type for constructing first from one arg, constructing second from remaining args      

第一个 tag 表示将给定的实参全部用来构造 _Ty2 对象,而 _Ty1 对象被值初始化。因此,使用了该 tag 的构造函数是这样实现的:

       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)...) {}      

此处仅仅展示主模板中该构造函数的定义,而特化中也有类似的定义,只不过初始化的对象不同而已。

第二个 tag 表示使用第一个实参构造 _Ty1 对象,使用剩余实参构造 _Ty2 对象,具体应用为:

       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)...) {}      

std::unique_ptr 就是使用该结构实现的:

       template <class _Ty, class _Dx /* = default_delete<_Ty> */> class unique_ptr { // non-copyable pointer to an object     ... private:     _Compressed_pair<_Dx, pointer> _Mypair; };      

_Ty1 就是删除器类型,_Ty2 就是 pointer 类型,而 pointerunique_ptr 的一个公有类型成员,就是要管理的指针类型。

至于 unique_ptr 不能复制,是因为其复制构造函数和复制赋值运算符都被弃置了:

       unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete;      

了解清楚 _Compressed_pair 的两个类型的对应关系后,我想其它成员函数也是比较好理解的。unique_ptr 代码也不长,可以试着读一读。




  

相关话题

  C/C++ 如何自定义printf函数的格式化符? 
  大型项目中面向过程思想 vs 面向对象思想,哪种开发效率更高? 
  既然scanf和strcpy等函数会被编译器报不安全,那么C语言教材为什么还讲这些函数? 
  何时使用 Protected 继承? 
  <<深度探索c++对象模型>>中的虚继承看着蛋疼,感觉这在实际中也没多大用,需要继续深究吗? 
  C/C++ 数组的下标为何要从 0 开始,而不从 1 开始? 
  3个小时,用 C++ 写不出AVL树,有些迷茫,怎么办? 
  C++ STL中的map用红黑树实现,搜索效率是O(lgN),为什么不像python一样用散列表从而获得常数级搜索效率呢? 
  为什么有些编程语言写完一句后要加分号? 
  只会c如何快速上手STL? 

前一个讨论
如何看待目前国内「隐私计算」行业现状,市场发展有哪些趋势?
下一个讨论
纳粹德国和法西斯意大利在二战前和战时是怎么互相看待彼此的(官方和民间)?





© 2024-11-22 - tinynew.org. All Rights Reserved.
© 2024-11-22 - tinynew.org. 保留所有权利