STL空间配置器-构造析构

2021-07-16  本文已影响0人  突击手平头哥

STL空间配置器-构造析构

STL空间配置器实际上包括两部分,一部分是空间的分配,另外一部分就是构造函数、析构函数的执行,这样才是newdelete的完整过程,以vector的操作作为说明

vector的插入操作

void push_back(const _Tp& __x = _STLP_DEFAULT_CONSTRUCTED(_Tp)) {
  if (this->_M_finish != this->_M_end_of_storage._M_data) {
    _Copy_Construct(this->_M_finish, __x);
    ++this->_M_finish;
  }
  else {
    typedef typename __type_traits<_Tp>::has_trivial_assignment_operator _TrivialCopy;
    _M_insert_overflow(this->_M_finish, __x, _TrivialCopy(), 1, true);
  }
}
template <class _Tp>
inline void _Copy_Construct(_Tp* __p, const _Tp& __val) {
  _Copy_Construct_aux(__p, __val, _Is_POD(__p)._Answer());
}

这里关注的主要是构造函数的执行,所以我们集中在_Copy_Construct_aux函数的执行,但是在这里遇到了第一个问题_Is_POD是什么意思?

POD类型是什么?

POD类型简单来说,指的是一种符合C语言内存布局的类型,支持字节赋值如memsetmemmove这样操作的类型;从上面push_back还难以看出,但是如果是进行批量插入、扩容复制操作时memmove的优越性就体现出来了

template <class _Tp>
inline void _Copy_Construct_aux(_Tp* __p, const _Tp& __val, const __false_type&) {
  new(__p) _Tp(__val);
}

template <class _Tp>
inline void _Copy_Construct_aux(_Tp* __p, const _Tp& __val, const __true_type&) {
  *__p = __val;
}

对于POD类型直接采用赋值的方式,而对于非POD类型采用的方式是调用了new函数;根据_IsPOD的返回值类型然后根据重载函数来执行对应的构造函数

在C++中是没有获取类型的接口的,哪怕是typeid获得的也仅仅是一个字符串而无法使用,这里就涉及到STL中非常重要的特性traits

如何进行类型推到?

很简单,利用了C++的模板机制

#include <iostream>

using namespace std;

template<class T>
class TestA {
public:
    typedef T value_type;
};

int main() {
    typename TestA<int>::value_type i = 10;
    cout << i << endl;
    return 0;
}

利用模板机制就可以推导出类似于代码中的value_type字段,这就是一个可以当作int之类使用的变量类型

#include <iostream>

using namespace std;

template<class T>
class TestA {
public:
    typedef T value_type;
};

template<class T>
void func(T t)  {
    typename TestA<T>::value_type i = 1.3;
    cout << i << endl;
}

int main() {
    func(1.3);
    return 0;
}

利用函数加模板的方式甚至可以推导变量的类型,通过函数推导变量类型、然后根据类型得到可使用的value_type

剩下具体判断是否是POD类型,就得看__type_traits

__type_traits的实现

template <class _Tp>
struct __type_traits {
   typedef __true_type     this_dummy_member_must_be_first;
#    if !defined (_STLP_HAS_TYPE_TRAITS_INTRINSICS)
   typedef __false_type    has_trivial_default_constructor;
   typedef __false_type    has_trivial_copy_constructor;
   typedef __false_type    has_trivial_assignment_operator;
   typedef __false_type    has_trivial_destructor;
   typedef __false_type    is_POD_type;
#    else
   typedef typename __bool2type<_STLP_HAS_TRIVIAL_CONSTRUCTOR(_Tp)>::_Ret has_trivial_default_constructor;
   typedef typename __bool2type<_STLP_HAS_TRIVIAL_COPY(_Tp)>::_Ret has_trivial_copy_constructor;
   typedef typename __bool2type<_STLP_HAS_TRIVIAL_ASSIGN(_Tp)>::_Ret has_trivial_assignment_operator;
   typedef typename __bool2type<_STLP_HAS_TRIVIAL_DESTRUCTOR(_Tp)>::_Ret has_trivial_destructor;
   typedef typename __bool2type<_STLP_IS_POD(_Tp)>::_Ret is_POD_type;
#    endif
};

普通的__type_traits默认不是POD类型,但是STL为原生类型实现了特异版本

_STLP_TEMPLATE_NULL
struct __type_traits_aux<__true_type> {
  typedef __true_type    has_trivial_default_constructor;
  typedef __true_type    has_trivial_copy_constructor;
  typedef __true_type    has_trivial_assignment_operator;
  typedef __true_type    has_trivial_destructor;
  typedef __true_type    is_POD_type;
};

#    define _STLP_DEFINE_TYPE_TRAITS_FOR(Type) \
_STLP_TEMPLATE_NULL struct __type_traits< Type > : __type_traits_aux<__true_type> {}; \
_STLP_TEMPLATE_NULL struct __type_traits< const Type > : __type_traits_aux<__true_type> {}; \
_STLP_TEMPLATE_NULL struct __type_traits< volatile Type > : __type_traits_aux<__true_type> {}; \
_STLP_TEMPLATE_NULL struct __type_traits< const volatile Type > : __type_traits_aux<__true_type> {}

#  ifndef _STLP_NO_BOOL
_STLP_DEFINE_TYPE_TRAITS_FOR(bool);
#  endif /* _STLP_NO_BOOL */
_STLP_DEFINE_TYPE_TRAITS_FOR(char);
#  ifndef _STLP_NO_SIGNED_BUILTINS
_STLP_DEFINE_TYPE_TRAITS_FOR(signed char);
#  endif
_STLP_DEFINE_TYPE_TRAITS_FOR(unsigned char);
#  if defined ( _STLP_HAS_WCHAR_T ) && ! defined (_STLP_WCHAR_T_IS_USHORT)
_STLP_DEFINE_TYPE_TRAITS_FOR(wchar_t);
#  endif /* _STLP_HAS_WCHAR_T */

_STLP_DEFINE_TYPE_TRAITS_FOR(short);
_STLP_DEFINE_TYPE_TRAITS_FOR(unsigned short);
_STLP_DEFINE_TYPE_TRAITS_FOR(int);
_STLP_DEFINE_TYPE_TRAITS_FOR(unsigned int);
_STLP_DEFINE_TYPE_TRAITS_FOR(long);
_STLP_DEFINE_TYPE_TRAITS_FOR(unsigned long);

#  ifdef _STLP_LONG_LONG
_STLP_DEFINE_TYPE_TRAITS_FOR(_STLP_LONG_LONG);
_STLP_DEFINE_TYPE_TRAITS_FOR(unsigned _STLP_LONG_LONG);
#  endif /* _STLP_LONG_LONG */

_STLP_DEFINE_TYPE_TRAITS_FOR(float);
_STLP_DEFINE_TYPE_TRAITS_FOR(double);

#  if !defined ( _STLP_NO_LONG_DOUBLE )
_STLP_DEFINE_TYPE_TRAITS_FOR(long double);
#  endif

对于一些原生类型实现了特异版本,is_POD_type定义为__true_type

总结

这里初步解析了STL构造器的实现,对于一般的类通过new语法实现,唯一需要注意的是这里使用的是原地构造

对于原生的如int类型,通过traits技术与一般的类区分开来,使用的更多是字节复制的操作

vector的批量操作

vector在进行扩容时需要对数据进行复制操作,以此作为例子

typedef typename __type_traits<_Tp>::has_trivial_assignment_operator _TrivialCopy;
_M_insert_overflow(this->_M_finish, __x, _TrivialCopy(), 1, true);

........

template <class _Tp, class _Alloc>
void vector<_Tp, _Alloc>::_M_insert_overflow(pointer __pos, const _Tp& __x, const __true_type& /*_TrivialCopy*/,
                                             size_type __fill_len, bool __atend ) {
  size_type __len = _M_compute_next_size(__fill_len);
  pointer __new_start = this->_M_end_of_storage.allocate(__len, __len);
  pointer __new_finish = __STATIC_CAST(pointer, _STLP_PRIV __copy_trivial(this->_M_start, __pos, __new_start));
  // handle insertion
  __new_finish = _STLP_PRIV __fill_n(__new_finish, __fill_len, __x);
  if (!__atend)
    __new_finish = __STATIC_CAST(pointer, _STLP_PRIV __copy_trivial(__pos, this->_M_finish, __new_finish)); // copy remainder
  _M_clear();
  _M_set(__new_start, __new_finish, __new_start + __len);
}

inline void*
__copy_trivial(const void* __first, const void* __last, void* __result) {
  size_t __n = (const char*)__last - (const char*)__first;
  return __n ? (void *)((char*)memmove(__result, __first, __n) + __n) : __result;
}

从之前可以看到has_trivial_assignment_operator对于原声类型是被定义为__true_type的,调用的是__copy_trivial函数,最终直接调用了memmove方法来执行,在批量操作时才能体现出traits的优势

template <class _T1, class _T2>
inline void _Param_Construct_aux(_T1* __p, const _T2& __val, const __true_type&) {
  // We use binary copying for POD types since it results
  // in a considerably better code at least on MSVC.
  *__p = _T1(__val);
}

而对于一般类型最终调用的是复制构造函数

上一篇 下一篇

猜你喜欢

热点阅读