本文基于vs2012中的std::make_shared函数进行分析,选取原因有两个,一是现在公司再用vs2012版本进行开发,我也就自然根据这个编译器进行的研究,第二是vs2012中并没有支持变长参数模板这个C++11的特性,但是make_shared仍然神奇的支持着“变长参数”,这就说明起码vs2012中的make_shared还没有用到变长参数就实现了这个功能,并且也导致一个问题,就是vs2013版本之后make_shared的实现方式可能发生了改变。所以在开始分析前,我要强调一下我所用的代码版本。
首先我认为还是要介绍一下make_shared这个函数,虽然可能对于大部分人来说这个根本没用,如果觉得没用可以直接跳过这个废话阶段。make_shared是C++11中创建shared_ptr这种智能指针的一种方式,并且实际上很推荐使用这种方式去创建一个智能指针,而不是直接通过原生指针去赋值或者通过shared_ptr的构造函数去创建一个智能指针。因为那样的操作略微存在一些内存泄漏上的风险,具体问题请自行百度shared_ptr。make_shared函数的一般使用方式是auto smart_ptr = std::make_shared<PTR_TYPE>(PARAM_TYPE1param1, PARAM_TYPE2 param2,...);参数就是指针实例化时构造函数需要的参数。而经过试验会发现这个参数数量最大是5,当然理由会在后面说明。
那接下来就正式开始分析这个函数。
首先通过跳转找到这个函数的定义,会在memory中看到这样一段代码
#define_ALLOCATE_MAKE_SHARED( \
TEMPLATE_LIST, PADDING_LIST,LIST, COMMA, X1, X2, X3, X4) \
template<class _Ty COMMA LIST(_CLASS_TYPE)> inline \
shared_ptr<_Ty>make_shared(LIST(_TYPE_REFREF_ARG)) \
{ /* make a shared_ptr */ \
_Ref_count_obj<_Ty> *_Rx =\
new _Ref_count_obj<_Ty>(LIST(_FORWARD_ARG)); \
shared_ptr<_Ty> _Ret; \
_Ret._Resetp0(_Rx->_Getptr(),_Rx); \
return (_Ret); \
} \
template<class _Ty, \
class _Alloc COMMA LIST(_CLASS_TYPE)> inline \
shared_ptr<_Ty>allocate_shared( \
const _Alloc& _Al_arg COMMA LIST(_TYPE_REFREF_ARG)) \
{ /* make a shared_ptr */ \
typedef _Ref_count_obj_alloc<_Ty,_Alloc> _Refoa; \
typename _Alloc::template rebind<_Refoa>::other _Alref =_Al_arg; \
_Refoa *_Rx = _Alref.allocate(1);\
_TRY_BEGIN \
::new (_Rx) _Refoa(_Al_arg COMMA LIST(_FORWARD_ARG)); \
_CATCH_ALL \
_Alref.deallocate(_Rx, 1); \
_RERAISE; \
_CATCH_END \
shared_ptr<_Ty> _Ret; \
_Ret._Resetp0(_Rx->_Getptr(),_Rx); \
return (_Ret); \
}
_VARIADIC_EXPAND_0X(_ALLOCATE_MAKE_SHARED, , , , )
#undef _ALLOCATE_MAKE_SHARED
这一段代码中可以找到长得很像make_shared的函数的定义,没错,那东西就是make_shared的定义。但是,这种代码,who understand?整个的函数定义都是包在一个宏里的,而这个宏的运行方式,可以用鬼畜来形容。这个定义实际上并不是c++11里的变长参数的实现方式,而是通过宏元编程,实现的代码生成,然后又通过代码生成,重载了6个make_shared函数。这6个函数分别接受0到5个参数,这也就是之前提到的最多接受5个参数的原因。
从结论上来讲,make_shared的定义是通过宏元实现的代码生成而来的重载函数,那么这个定义到底是如何产生的?其实如果熟悉宏的操作的话,会很清楚的了解到这个定义整个是包在一个_ALLOCATE_MAKE_SHARED的宏里的,而这个宏在最后_VARIADIC_EXPAND_0X(_ALLOCATE_MAKE_SHARED