STL空间配置器-构造析构
STL空间配置器实际上包括两部分,一部分是空间的分配,另外一部分就是构造函数、析构函数的执行,这样才是
new
和delete
的完整过程,以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语言内存布局的类型,支持字节赋值如memset
、memmove
这样操作的类型;从上面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);
}
而对于一般类型最终调用的是复制构造函数
网友评论