#include <memory>
#include <iostream>
#include <cstdio>
#pragma optimize("",off)
char* g_temp_test_valid = nullptr;
std::shared_ptr<char[]> getMem() {
std::shared_ptr<char[]> pOut(new char[10]);
strcpy_s(pOut.get(), 10,"hello");
g_temp_test_valid = pOut.get();
return pOut;
}
void testSharedPtr()
{
{
auto ret = getMem();
printf("before [%s]\r\n", g_temp_test_valid);
}
try
{
printf("after [%s]\r\n", g_temp_test_valid);
printf("exits");
}
catch (std::exception & e)
{
printf("%s", e.what());
printf("exception\r\n");
}
}
int main()
{
testSharedPtr();
return 0;
}
有两个申请操作,一个存储 new char[10],一个存储引用计数、new char[10]、deleter
这就是https://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/ 所说的overhead。
为了验证文中观点,我们看下std::make_share 的内存结构
参见:https://www.cnblogs.com/apocelipes/p/10346928.html,由于c++ 17 不支持std::make_share<char[]>(10);这样的语法,我们用一个自定义的数据类型好了:
#include <memory>
#include <iostream>
#include <cstdio>
#pragma optimize("",off)
template<size_t t>
struct DATA
{
char data[t];
};
char* g_temp_test_valid = nullptr;
std::shared_ptr<DATA<40>> getMemByMakeShare()
{
auto pOut = std::make_shared<DATA<40>>();
g_temp_test_valid = pOut.get()->data;
strcpy_s(pOut.get()->data, "hello");
return pOut;
}
void testSharedPtr()
{
{
auto ret = getMemByMakeShare();
printf("before [%s]\r\n", g_temp_test_valid);
}
try
{
printf("after [%s]\r\n", g_temp_test_valid);
printf("exits");
}
catch (std::exception & e)
{
printf("%s", e.what());
printf("exception\r\n");
}
}
int main()
{
testSharedPtr();
return 0;
}
实现为:
// FUNCTION TEMPLATE make_shared
template <class _Ty, class... _Types>
_NODISCARD shared_ptr<_Ty> make_shared(_Types&&... _Args) { // make a shared_ptr
const auto _Rx = new _Ref_count_obj2<_Ty>(_STD forward<_Types>(_Args)...);
shared_ptr<_Ty> _Ret;
_Ret._Set_ptr_rep_and_enable_shared(_STD addressof(_Rx->_Storage._Value), _Rx);
return _Ret;
}
// CLASS TEMPLATE _Ref_count_obj2
template <class _Ty>
class _Ref_count_obj2 : public _Ref_count_base { // handle reference counting for object in control block, no allocator
public:
template <class... _Types>
explicit _Ref_count_obj2(_Types&&... _Args) : _Ref_count_base() {
_Construct_in_place(_Storage._Value, _STD forward<_Types>(_Args)...);
}
~_Ref_count_obj2() {
// nothing to do, _Storage._Value was already destroyed in _Destroy
}
union {
_Wrap<_Ty> _Storage;//这里显然,是包含了类的内存,注意本类的声明是继承了Ref_count_base,因此,内存结构是铁板一块
};
private:
virtual void _Destroy() noexcept override { // destroy managed resource
_Destroy_in_place(_Storage._Value);// 调用析构函数
}
virtual void _Delete_this() noexcept override { // destroy self
delete this;// 真正释放内存
}
};
基类还是我们熟悉的,引用计数的基类
// CLASS _Ref_count_base
class __declspec(novtable) _Ref_count_base { // common code for reference counting
private:
#ifdef _M_CEE_PURE
// permanent workaround to avoid mentioning _purecall in msvcurt.lib, ptrustu.lib, or other support libs
virtual void _Destroy() noexcept {
_STD terminate();
}
virtual void _Delete_this() noexcept {
_STD terminate();
}
#else // ^^^ _M_CEE_PURE / !_M_CEE_PURE vvv
virtual void _Destroy() noexcept = 0; // destroy managed resource
virtual void _Delete_this() noexcept = 0; // destroy self
#endif // _M_CEE_PURE
_Atomic_counter_t _Uses = 1;
_Atomic_counter_t _Weaks = 1;
protected:
constexpr _Ref_count_base() noexcept = default; // non-atomic initializations
public:
_Ref_count_base(const _Ref_count_base&) = delete;
_Ref_count_base& operator=(const _Ref_count_base&) = delete;
virtual ~_Ref_count_base() noexcept {} // TRANSITION, should be non-virtual
bool _Incref_nz() noexcept { // increment use count if not zero, return true if successful
auto& _Volatile_uses = reinterpret_cast<volatile long&>(_Uses);
long _Count = _ISO_VOLATILE_LOAD32(_Volatile_uses);
while (_Count != 0) {
const long _Old_value = _INTRIN_RELAXED(_InterlockedCompareExchange)(&_Volatile_uses, _Count + 1, _Count);
if (_Old_value == _Count) {
return true;
}
_Count = _Old_value;
}
return false;
}
void _Incref() noexcept { // increment use count
_MT_INCR(_Uses);
}
void _Incwref() noexcept { // increment weak reference count
_MT_INCR(_Weaks);
}
void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}
void _Decwref() noexcept { // decrement weak reference count
if (_MT_DECR(_Weaks) == 0) {
_Delete_this();
}
}
long _Use_count() const noexcept {
return static_cast<long>(_Uses);
}
virtual void* _Get_deleter(const type_info&) const noexcept {
return nullptr;
}
};
template <class _Ux>
void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px
this->_Ptr = _Px;
this->_Rep = _Rx;
#if _HAS_IF_CONSTEXPR
if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {
if (_Px && _Px->_Wptr.expired()) {
_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
}
}
#else // ^^^ _HAS_IF_CONSTEXPR // !_HAS_IF_CONSTEXPR vvv
_Enable_shared_from_this1(*this, _Px,
bool_constant<
conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>>{});
#endif // _HAS_IF_CONSTEXPR
}
这样就很清晰的对应了std::make_shared 对应的语义,只申请整块内存,同时存放引用计数和对象,不可定义deleter(因为这里用的是placed new 和 placed delete。
对于小块类来说挺好,对大块的内存,会导致大块内存不被释放。