接口使用
#include <iostream>
#include <memory>
#include <type_traits>
struct Foo
{
Foo(int i) : i(i) {}
int i;
};
int main()
{
auto sp1 = std::make_shared<Foo>(12); // 方式1
std::shared_ptr<Foo> sp2(new Foo(30)); // 方式2
static_assert(std::is_same<decltype(sp1), std::shared_ptr<Foo>>::value);
static_assert(std::is_same<decltype(sp2), std::shared_ptr<Foo>>::value);
std::cout << sp1->i << '\n';
std::cout << sp2->i << '\n';
}
- 通过方式2,是先在堆上分配一块内存,然后在堆上再建一个智能指针控制块,这两个东西是不连续的,会造成内存碎片化
- 通过方式1,是直接在堆上新建一块足够大的内存,其中包含两部分,上面是内存(用来使用),下面是控制块(包含引用计数),然后用Foo的构造函数去初始化分配的内存(分配一块内存的步骤:先分配内存,再进分配的内存调用构造函数进行构造,构造完毕才能使用)
new方式
// 构造函数初始化列表_M_refcount(__p) 这里会进行内存分配
template<typename _Tp1>
explicit __shared_ptr(_Tp1* __p)
: _M_ptr(__p), _M_refcount(__p)
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
static_assert( sizeof(_Tp1) > 0, "incomplete type" );
__enable_shared_from_this_helper(_M_refcount, __p, __p);
}
template<typename _Ptr>
explicit
__shared_count(_Ptr __p) : _M_pi(0)
{
__try
{
_M_pi = new _Sp_counted_ptr<_Ptr, _Lp>(__p);
}
__catch(...)
{
delete __p;
__throw_exception_again;
}
}
make_shared接口
auto sp1 = std::make_shared<Foo>(12);
调用堆栈
–> make_shared
–> allocate_shared
--> __shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a, _Args&&... __args)
–> operator=
// ref: gcc-4.8.5\libstdc++-v3\include\bits\shared_ptr.h
/**
* @brief Create an object that is owned by a shared_ptr.
* @param __args Arguments for the @a _Tp object's constructor.
* @return A shared_ptr that owns the newly created object.
* @throw std::bad_alloc, or an exception thrown from the
* constructor of @a _Tp.
*/
template<typename _Tp, typename... _Args>
inline shared_ptr<_Tp>
make_shared(_Args&&... __args)
{
typedef typename std::remove_const<_Tp>::type _Tp_nc;
return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
std::forward<_Args>(__args)...);
}
/**
* @brief Create an object that is owned by a shared_ptr.
* @param __a An allocator.
* @param __args Arguments for the @a _Tp object's constructor.
* @return A shared_ptr that owns the newly created object.
* @throw An exception thrown from @a _Alloc::allocate or from the
* constructor of @a _Tp.
*
* A copy of @a __a will be used to allocate memory for the shared_ptr
* and the new object.
*/
template<typename _Tp, typename _Alloc, typename... _Args>
inline shared_ptr<_Tp>
allocate_shared(const _Alloc& __a, _Args&&... __args)
{
return shared_ptr<_Tp>(_Sp_make_shared_tag(), __a,
std::forward<_Args>(__args)...);
}
template<class _Tp1>
shared_ptr&
operator=(shared_ptr<_Tp1>&& __r) noexcept
{
this->__shared_ptr<_Tp>::operator=(std::move(__r));
return *this;
}
template<typename _Alloc, typename... _Args>
__shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
_Args&&... __args)
: _M_ptr(), _M_refcount()
{
typedef typename _Alloc::template rebind<_Tp>::other _Alloc2;
_Deleter<_Alloc2> __del = { _Alloc2(__a) };
typedef allocator_traits<_Alloc2> __traits;
_M_ptr = __traits::allocate(__del._M_alloc, 1);
__try
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2070. allocate_shared should use allocator_traits<A>::construct
__traits::construct(__del._M_alloc, _M_ptr,
std::forward<_Args>(__args)...);
}
__catch(...)
{
__traits::deallocate(__del._M_alloc, _M_ptr, 1);
__throw_exception_again;
}
__shared_count<_Lp> __count(_M_ptr, __del, __del._M_alloc);
_M_refcount._M_swap(__count);
__enable_shared_from_this_helper(_M_refcount, _M_ptr, _M_ptr);
}
性能测试
测试用例
#include <iostream>
#include <memory>
#include <time.h>
struct Foo
{
Foo(int i) : i(i) {}
int i;
short b;
char buf[4];
char arr[28];
bool check;
};
uint64_t timespec_diff(timespec t1, timespec t2)
{
return (t2.tv_sec - t1.tv_sec) * 1000000000ULL + t2.tv_nsec - t1.tv_nsec;
}
int main(int argc, char* argv[])
{
constexpr int kLoop = 10000;
timespec t1, t2, t3;
clock_gettime(CLOCK_MONOTONIC, &t1);
for (int i = 0; i < kLoop; ++i)
{
auto sp1 = std::make_shared<Foo>(22);
}
clock_gettime(CLOCK_MONOTONIC, &t2);
for (int i = 0; i < kLoop; ++i)
{
std::shared_ptr<Foo> sp2(new Foo(22));
}
clock_gettime(CLOCK_MONOTONIC, &t3);
std::cout << "new cost time: " << timespec_diff(t2, t3) * 1.0 / kLoop << "ns" << std::endl;
std::cout << "make_shared cost time: " << timespec_diff(t1, t2) * 1.0 / kLoop << "ns" << std::endl;
return 0;
}
时延数据
场次 | new | make_shared |
---|---|---|
-O0优化 | 109.873ns | 286.667ns |
-O1优化 | 48.7914ns | 26.6106ns |
-O2优化 | 49.4341ns | 28.6477ns |
从测试数据看出,make_shared比new快一倍左右。