独占型智能指针
auto_ptr由于在拷贝和赋值时会转移指针权限,无法在STL容器中使用,在c++17中已经删除
unique_ptr为独占式智能指针,无法进行拷贝和赋值,这里我们利用RAII机制来实现一个简单的unique_ptr
这里使用泛型编程,unique_ptr内部保存T指针即可,在构造函数保存指针,析构函数释放内存
template<typename T>
class unique_ptr
{
public:
unique_ptr(T *ptr = nullptr) : ptr_(ptr)
{
}
~unique_ptr()
{
Release();
}
private:
void Release()
{
if(ptr_)
delete ptr_;
}
private:
T *ptr_;
};
独占式智能指针,所以我们将拷贝构造函数和赋值运算符禁用,并且加入移动构造函数和移动赋值运算符
template<typename T>
class unique_ptr
{
pubilc:
//移动构造,不要忘了noexcept
unique_ptr(unique_ptr<T> &&rhs) noexcept : ptr_(rhs.ptr_)
{
rhs.ptr_ = nullptr;
}
unique_ptr<T>& operator=(unique_ptr<T> &&rhs)
{
//先释放自己管理的指针
Release();
//保存rhs的指针
ptr_ = rhs.ptr_;
//将rhs的指针设为空
rhs.ptr_ = nullptr;
}
//禁用拷贝构造
unique_ptr(const unique_ptr<T> &) = delete;
void operator=(const unique_ptr<T>&) = delete;
};
再加入一些指针的运算符重载,让使用者能够访问指针
template<typename T>
class unique_ptr{
public:
void reset(T *ptr = nullptr)
{
//释放自身管理的指针
Release();
//
ptr_ = ptr;
}
void swap(unique_ptr<T> &rhs)
{
//交换两个unique_ptr内部指针即可
std::swap(ptr_,rhs.ptr_);
}
void get() const{ return ptr_; }
T* operator->() { return ptr_; }
T& operator*() { return *ptr_; }
operator bool() { return !!ptr_; }
};
最后我们使用可变参数模板和完美转发实现make_unique,这样就完成了一个基本的unique_ptr了
/**
* @description: 创建一个unique_ptr
* @param {T} 对象类型
* @param {Args...} 构造对象的参数
* @return {*}
*/
template<typename T,typename ...Args>
unique_ptr<T> make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
完整代码如下:
template<typename T>
class unique_ptr
{
public:
unique_ptr(T *ptr = nullptr) : ptr_(ptr)
{
}
~unique_ptr()
{
Release();
}
unique_ptr(unique_ptr<T> &&rhs) noexcept : ptr_(rhs.ptr_)
{
rhs.ptr_ = nullptr;
}
unique_ptr<T>& operator=(unique_ptr<T> &&rhs)
{
Release();
ptr_ = rhs.ptr_;
rhs.ptr_ = nullptr;
}
void reset(T *ptr = nullptr)
{
Release();
ptr_ = ptr;
}
void swap(unique_ptr<T> &rhs)
{
std::swap(ptr_,rhs.ptr_);
}
void get() const{ return ptr_; }
T* operator->() { return ptr_; }
T& operator*() { return *ptr_; }
operator bool() { return !!ptr_; }
unique_ptr(const unique_ptr<T> &) = delete;
void operator=(const unique_ptr<T>&) = delete;
private:
void Release()
{
if(ptr_)
delete ptr_;
}
private:
T *ptr_;
};
/**
* @description: 创建一个unique_ptr
* @param {T} 对象类型
* @param {Args...} 构造对象的参数
* @return {*}
*/
template<typename T,typename ...Args>
unique_ptr<T> make_unique(Args&&... args)
{
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
共享型智能指针
线程安全
先来分析一下标准库shared_ptr的线程安全问题,我们都知道shared_ptr是引用计数型智能指针,能够在多线程下安全释放内存,但这并不代表多个线程访问同一个shared_ptr是线程安全的
我们来看看多线程访问同一个shared_ptr的示例(此代码运行会coredump)
//全局的智能指针
std::shared_ptr<int> num(new int(10));
void test04()
{
//创建一个子线程访问智能指针
std::thread t([]()
{
//死循环读取num的值
for(;;)
{
auto it = num;
int tmp = *num;
}
});
//主线程休眠一秒
sleep(1);
//将智能指针reset
num.reset();
}
- 在主线程休眠后,此时子线程正在对shared_ptr进行读操作,主线程对shared_ptr进行写操作(reset),此时的状态如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4uTqb9S1-1639643217240)(https://note.youdao.com/yws/res/10266/WEBRESOURCEc1acd7b34877386670966059f66ad0d4)]
- 由于主线程将shared_ptr进行reset,导致shared_ptr将num的内存进行释放,而此时子线程又刚好在访问num,这就访问内存非法coredump了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ps5fJ49p-1639643217242)(https://note.youdao.com/yws/res/10260/WEBRESOURCE179ca3c7d09abf1e42bcd2de0d1d8560)]
我们可以得出以下结论:
- 多个线程对同一个shared_ptr进行读写是线程不安全的,拷贝,访问等都是读操作,析构,移动,reset都是写操作
- 多个线程对同一个shared_ptr进行读操作是线程安全的
- 多个线程对共享同一个引用计数的不同shared_ptr进行读写操作是线程安全的
shared_ptr
网上大部分都是实现一个简单非线程安全引用计数的shared_ptr,这里我们参考标准库实现一个线程安全含有强弱引用计数的shared_ptr,完成shared_ptr和weak_ptr的全部功能,再也不怕面试问智能指针了
引用计数
这里我们使用c++11提供atomic来实现引用计数对象,包含强弱两个引用计数,对外提供增加和减少引用计数的接口即可
class SharedRefCount
{
public:
SharedRefCount() : WeakRefCountImpl_(1),
RefCountImpl_(1)
{}
~SharedRefCount() = default;
/**
* @description: 将引用计数加一
* @param {*}
* @return {*}
*/
void IncrementRef() { ++RefCountImpl_; }
/**
* @description: 将引用计数减一,并获取原子操作减一后的值
* @param {*}
* @return {long} 引用计数值
*/
long DecrementRef() { return --RefCountImpl_; }
/**
* @description: 将弱引用计数加一
* @param {*}
* @return {*}
*/
void IncrementWeak() { ++WeakRefCountImpl_; }
/**
* @description: 将弱引用计数减一,并获取原子操作减一后的值
* @param {*}
* @return {long} 弱引用计数值
*/
long DecrementWeak() { return --WeakRefCountImpl_;}
/**
* @description: 获取强引用计数值
* @param {*}
* @return {long} 强引用计数值
*/
long use_count() { return RefCountImpl_.load(std::memory_order_acquire)}
private:
std::atomic<long> WeakRefCountImpl_;
std::atomic<long> RefCountImpl_;
};
我们再提供一个lock接口,在强引用计数不为0的情况下将强引用计数加一,这里需要用到CAS操作
/**
* @description: 在强引用计数不为0的情况下进行加一
* @param {*}
* @return {bool} 1 成功 | 0 失败
*/
bool lock()
{
//获取单个变量,这里我们用relaxed松散型内存序即可
long num = RefCountImpl_.load(std::memory_order_relaxed);
//CAS操作使用while循环不断获取引用计数的值,如果大于0则继续尝试加一
do
{
if(num == 0)
return false;
//CAS失败后,会将原子变量值保存在num中
}while(!RefCountImpl_compare_exchange_weak(num,num+1,std::memeory_acq_rel,std::memory_order_relaxed));
return true;
}
内存序
* @param memory_order_relaxed 只保证自身操作为原子性,不会影响其他变量的读写操作顺序
* @param memory_order_consume 用于读操作,对于内存序的影响:当前线程中consume操作之后依赖于该consume操作不能排到该consume操作之前
* @param memory_order_acquire 用于读操作,对于内存序的影响:当前线程中acquire之后的读写操作不能排到acquire之前
* 其他线程对于该原子变量release操作以及之前的写入都在acquire操作之后可见
* @param memory_order_release 适用于写操作,对于内存序的影响:该操作之前的读写都不能排到该操作之后
* release写入的数据,对于其他线程对该原子变量的acquire操作和release之后的操作可见
* @param memory_order_acq_rel 即是acquire操作也是release操作,read-modify-write操作
* @param memory_order_seq_cst 用于load,release,read-modify-write操作,读操作时为acquire,写操作时为release,读-改-写操作时为acq_rel
*/
Attribute
实现shared_ptr需要三个成员变量
- 对象指针
- 引用计数指针
- 删除器
template<typename T>
class shared_ptr
{
//std::function使用类型擦除来提供强大的泛型能力,可以保存lambda表达式,函数指针,仿函数
using Deleter = std::function<void(T*)>;
private:
//对象指针
T *ptr_;
//引用计数指针
SharedRefCount *RefCount_;
//自定义删除器
Deleter deleter_;
};
Method
shared_ptr是可以进行拷贝和移动的,所以我们要为其提供拷贝构造函数和移动构造函数
template<typename T>
class shared_ptr
{
public:
//默认构造函数
shared_ptr(T *ptr = nullptr,const Deleter &deleter = Deleter()) :
ptr_(ptr),
deleter_(deleter)
{
if(!_ptr)
return;
//对象指针不为空,那么就构造引用计数对象
RefCount_ = new SharedRefCount();
}
//拷贝构造函数
shared_ptr(const shared_ptr<T> &rhs) : ptr_(rhs.ptr_),
RefCount_(rhs.RefCount_),
deleter_(rhs.deleter_)
{
//对象指针不为空,那么就将引用计数加一
if(ptr_)
RefCount_->IncrementRef();
}
//移动构造函数
shared_ptr(shared<T> &&rhs) noexcept : ptr_(rhs.ptr_),
RefCount_(rhs.RefCount_),
deleter_(rhs.deleter_)
{
//将移动的对象置空
rhs.ptr_ = nullptr;
rhs.RefCount_ = nullptr;
rhs.deleter_ = Deleter();
}
};
提供两个构造函数模板,使我们的智能指针也能让父类指针指向子类对象,实现多态!
template<typename T>
class shared_ptr
{
public:
//拷贝构造函数模板
template<typename U>
shared_ptr(const shared_ptr<U> &rhs,T *ptr) : ptr_(ptr_),
RefCount_(rhs.RefCount_)
{
if(ptr_)
RefCount_->IncrementRef();
}
//构造函数模板
template<typename U>
shared_ptr(U *ptr = nullptr,const Deleter &deleter = Deleter()) :
ptr_(ptr),
deleter_(deleter)
{
if(!_ptr)
return;
//对象指针不为空,那么就构造引用计数对象
RefCount_ = new SharedRefCount();
}
};
/**
* @description: 将shared_ptr<U> 转换为 shared_ptr<T>
* @param {T} 目标类型
* @param {U} 源类型
* @return {shared_ptr<T>} 转换后的智能指针
*/
template<typename T,typename U>
shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U> &rhs)
{
//使用dynamic_cast安全转换指针类型
T *ptr = dynamic_cast<T*>(rhs.get());
return shared_ptr<T>(rhs,ptr);
}
使用方法如下:
//A : Base B : Drivered
KH::shared_ptr<A> p1(new B());
KH::shared_ptr<B> p2(new B());
KH::shared_ptr<A> p3 = KH::dynamic_pointer_cast<A>(p2);
相应的我们应该也提供赋值运算符和移动赋值运算符
//这里我们使用了一点小技巧,参数不再是引用对象
shared_ptr<T>& operator=(shared_ptr<T> rhs)
{
rhs.swap(*this);
return *this;
}
shared_ptr<T>& operator=(shared_ptr<T> &&rhs)
{
//先释放自身管理的指针
Release();
//浅拷贝
ptr_ = rhs.ptr_;
deleter_ = std::move(rhs.deleter_);
RefCount_ = rhs.RefCount_;
//将rhs设为空
rhs.ptr_ = nullptr;
RefCount_ = nullptr;
rhs.deleter_ = Deleter();
return *this;
}
这里我们重点讲一下赋值运算符的写法,这里我们要考虑到异常安全和自我赋值的情况,我们先看看下面这种异常不安全的写法 :
- 判断是否为自我赋值
- 释放自身管理的指针
- 拷贝对象
shared_ptr<T>& opeartor(const shared_ptr<T> &rhs)
{
//1. 判断是否为自我赋值
if(&rhs == this)
return;
//2. 释放自身管理的指针
Release();
//3. 拷贝对象
ptr_ = rhs.ptr_;
RefCount_ = rhs.RefCount_;
deleter_ = rhs.deleter_;
if(ptr_)
RefCount_->IncrementRef();
}
如果在拷贝时出现异常,导致拷贝失败那么这个shared_ptr就处于一种中间状态,因为在拷贝前我们将自身的指针释放了,而上面的写法即使拷贝失败,我们还是处于原来的状态,并有破坏原有结构,我们通过构造一个参数的临时对象来进行拷贝,拷贝完成后再进行swap,这样就能保证异常安全。
这里我们再来看看std::swap的实现,它的实现也是异常安全的
template<typename T>
void swap(T &lhs,T &rhs)
{
//通过移动对象来避免拷贝
T tmp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(tmp);
}
同样我们应该提供一个swap函数来交换两个shared_ptr
void swap(shared_ptr<T> &rhs)
{
std::swap(ptr_,rhs.ptr_);
std::swap(RefCount_,rhs.RefCount_);
std::swap(deleter_,rhs.deleter_);
}
我们再来完善shared_ptr的其他接口,实现shared_ptr的全部功能
long use_count()
{
return RefCount_ ? RefCount_->use_count() : 0;
}
T* get() const{ return ptr_; }
void reset(T * ptr = nullptr)
{
Release();
ptr_ = ptr;
if(ptr_)
RefCount_ = new SharedRefCount();
}
bool unique()
{
return RefCount_ ? RefCount_->use_count() == 1 : false;
}
operator bool() { return !!ptr_; }
T* operator->() const{ return ptr_; }
T& operator*() const{ return *ptr_;}
bool operator==(const shared_ptr<T> &rhs) const { return ptr_ == rhs.ptr_;}
bool operator!=(const shared_ptr<T> &rhs) const { return ptr_ != rhs.ptr_;}
这里我们通过可变参模板和完美转发来简单实现一个make_shared,标准库的实现更加复杂一些,只进行一次内存分配将引用计数和对象一起构造,减少了内存碎片的产生
template<typename T,typename ...Args>
inline shared_ptr<T> make_shared(Args&& ...args)
{
return shared_ptr<T>(std::forward(args)...);
}
引用计数对象释放问题
对象的删除策略很简单,只需要在强引用计数为0时释放对象即可,但是不要忘了我们还需要将引用计数对象也进行释放,显然我们不能在强引用计数为0时就释放引用计数对象,因为这时可能还有weak_ptr在监视,如果我们直接将引用计数对象释放了,那么weak_ptr进行访问就会coredump,那我们能否在强弱引用计数都为0的时候释放呢?
我们来分析一下能否在强弱引用计数都为0时释放引用计数对象
- 此时有一个shared_ptr和一个监视的weak_ptr在两个不同的线程,强弱引用计数都为1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PFwQyJsb-1639643217242)(https://note.youdao.com/yws/res/10265/WEBRESOURCE067fd0a04d03cb7077b640cb4aa00175)]
- 两个线程同时释放shared_ptr和weak_ptr, shared_ptr通过CAS操作将RefCountImpl减1,weak_ptr通过CAS操作将WeakRefCountImpl减一
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g1ai0YIw-1639643217243)(https://note.youdao.com/yws/res/10258/WEBRESOURCE6f0490d52c8b4d6551b9b0027a02cc79)]
-
此时强弱引用计数都为0,那么shared_ptr和weak_ptr都会去释放引用计数对象,就会产生double free的异常情况
-
我们仔细思考一下,应该在强弱引用计数都为0时释放对象,这说明强弱引用计数两个变量减一为0应该为原子操作,那仅通过单一变量的原子操作是没办法做到的,我们可以使用锁来保护临界区,这确实是一种解决办法,但是标准库却仅使用原子操作巧完成了引用计数对象的释放
解决方案: 在弱引用计数为0时释放引用计数对象
我们回顾一下引用计数对象的实现,在构造函数中我们将RefCountImpl初始化为1,因为我们只会在shared_ptr里构造引用计数对象,所以强引用计数初始为1。这里我们将WeakRefCountImpl也初始化为1,这个1是用来监控是否还有shared_ptr对象存在,只有在最后一个shared_ptr释放时,将强引用计数减为0时,才会将这个1减去。
class SharedRefCount
{
public:
SharedRefCount() : WeakRefCountImpl_(1),
RefCountImpl_(1)
{}
};
我们再来分析一下这个情况
1.还是一样此时有一个shared_ptr和一个监视的weak_ptr在两个不同的线程,区别是强引用计数为1,弱引用计数为2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-93sWuCFP-1639643217244)(https://note.youdao.com/yws/res/10252/WEBRESOURCEa60ce6ca57c468a253a6160c2f874992)]
2.此时shared_ptr和weak_ptr同时释放,shared_ptr将强引用计数减一,weak_ptr将弱引用计数减一,此时强引用计数为0,弱引用计数为1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dulq2WHH-1639643217244)(https://note.youdao.com/yws/res/10264/WEBRESOURCEb31cce80f8132ba115103400a52a4454)]
3.weak_ptr发现弱引用计数为1则没有去释放对象,而此时shared_ptr发现已经将强引用计数减为0了,则会去减去最后一个监视的弱引用计数,发现弱引用计数为0了,则释放引用计数对象
,这样就完美解决了引用计数对象double free的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NOwIIyex-1639643217245)(https://note.youdao.com/yws/res/10254/WEBRESOURCEee37a34da08bff970e6704a55dc4626a)]
最后我们将释放操作的代码完成,至此就实现了一个完整的shared_ptr了
void destory_obj()
{
//有自定义删除器
if(deleter_)
{
deleter_(ptr_);
}else
{
//没有删除器则使用delete
delete ptr_;
}
}
void Release()
{
//空指针无需操作
if(!ptr_)
return;
//将强引用计数减为0进行释放操作
if(RefCount_->DecrementRef() == 0)
{
destory_obj();
//将弱引用计数减一,减一后为0则将引用计数对象释放
if(RefCount_->DecrementWeak() == 0)
delete RefCount_;
}
ptr_ = nullptr;
RefCount_ = nullptr;
}
weak_ptr
weak_ptr的成员变量与shared_ptr一样,但是weak_ptr仅用来监控shared_ptr,并不掌控对象的生命周期
template<typename T>
class weak_ptr
{
public:
using Deleter = std::function<void(T*)>;
private:
T *ptr_;
SharedRefCount *RefCount_;
Deleter deleter_;
};
weak_ptr的实现与shared_ptr类似,每次拷贝时将弱引用计数加一,析构时将弱引用计数减一即可
template<typename T>
class weak_ptr
{
public:
weak_ptr() : ptr_(nullptr),
RefCount_(nullptr){}
weak_ptr(const shared_ptr<T> &rhs) : ptr_(rhs.ptr_),
RefCount_(rhs.RefCount),
deleter_(rhs.deleter_)
{
//增加弱引用计数
if(RefCount_)
{
RefCount_->IncreamentWeak();
}
}
//对weak_ptr拷贝
weak_ptr(const weak_ptr<T> &rhs) : ptr_(rhs.ptr_),
RefCount_(rhs.RefCount_),
deleter_(rhs.deleter_)
{
//增加弱引用计数
if(RefCount_)
{
RefCount_->IncreamentWeak();
}
}
//移动构造
weak_ptr(weak_ptr<T> &&rhs) noexcept : ptr_(rhs.ptr_),
RefCount_(rhs.RefCount_),
deleter_(rhs.deleter_)
{
rhs.ptr_ = nullptr;
rhs.RefCount_ = nullptr;
deleter_ = Deleter();
}
//赋值运算符
weak_ptr& operator=(weak_ptr<T> rhs)
{
//临时对象析构时会释放
rhs.swap(*this);
return *this;
}
weak_ptr& operator=(const shared_ptr<T> &rhs)
{
//构造临时对象进行交换,保证异常安全
weak_ptr(rhs).swap(*this);
return *this;
}
//交换两个weak_ptr
void swap(weak_ptr<T> &rhs)
{
std::swap(ptr_,rhs.ptr_);
std::swap(RefCount_,rhs.RefCount_);
std::swap(deleter_,rhs.deleter_);
}
};
我们再来实现expried和lock两个接口,用来判断对象是否过期和构造一个shared_ptr
bool expried()
{
//空指针
if(!ptr_)
return true;
//强引用计数是为0则expried
return RefCount_ ? RefCount_->use_count() == 0 : true;
}
shared_ptr<T> lock()
{
//已经expried,就返回一个空shared_ptr
if(expried())
return shared_ptr<T>();
//使用自身构造一个shared_ptr
return shared_ptr<T>(*this);
}
释放操作跟shared_ptr类似,每次析构时将弱引用计数减一,弱引用计数为0时则释放引用计数对象
void Release()
{
if(!ptr_ || !RefCount_)
return;
//弱引用计数减为0则释放引用计数
if(RefCount_->DecreamentWeak() == 0)
delete RefCount_;
ptr_ = nullptr;
RefCount_ = nullptr;
deleter_ = Deleter();
}
enable_shared_from_this
有时候我们需要在对象内部返回一个shared_ptr,这时我们便需要使用enable_shared_from_this这个小工具了,实现思路也很简单,我们只需要在enable_shared_from_this中保存一个weak_ptr监控外部的shared_ptr即可,在需要时就可以使用这个weak_ptr进行构造了
template<typename T>
class enable_shared_from_this
{
public:
enable_shared_from_this() = default;
~enable_shared_from_this() = default;
enable_shared_from_this(const enable_shared_from_this &rhs) : _M_weak_this(rhs._M_weak_this)
{
}
shared_ptr<T> shared_from_this()
{
return shared_ptr<T>(_M_weak_this);
}
weak_ptr<T> shared_from_this_weak()
{
return _M_weak_this;
}
private:
//内部weak_ptr用于监控外部shared_ptr
weak_ptr<T> _M_weak_this;
};
enable_shared_from_this类的实现非常简单,但是我们面临一个问题,应该在什么时候去初始化这个内部的weak_ptr呢,这里就需要用到c++元编程的知识了,这个weak_ptr是用来监视shared_ptr,所以肯定得在shared_ptr的构造函数中去初始化这个weak_ptr,在编译期判断T是否含有_M_weak_this这个成员变量,如果含有这个成员变量则生成初始化这个变量的代码
由于_M_weak_this为私有成员,所以这里需要再写一个enable_shared_helper友元类,来进行编译期间的判断
//泛化版本类模板,无法推断则继承std::false_type,生成编译期常量value = false
template<typename T,typename = void_t<>>
struct enable_shared_helper : std::false_type
{};
//类模板偏特化,如果能推断出T::_M_weak_this,则继承std::true_type,会生成一个编译期常量value = true
template<typename T>
struct enable_shared_helper<T,void_t<decltype(T::_M_weak_this)>> : std::true_type
{};
有了enable_shared_helper,我们便可以使用c++17的if constexpr在编译期进行判断并初始化weak_ptr了,我们修改一下shared_ptr的构造函数
shared_ptr(T *ptr = nullptr,const Deleter &deleter = Deleter()) :
ptr_(ptr),
RefCount_(nullptr),
deleter_(deleter)
{
if(!ptr_)
return;
RefCount_ = new SharedRefCount();
//编译期判断T是否含有_M_weak_this
if constexpr(enable_shared_helper<T>::value)
{
//含有此成员,则进行初始化
ptr_->_M_weak_this = *this;
}
}
我们再来测试一下enable_shared_from_this的功能
class A : public KH::enable_shared_from_this<A>
{
public:
A(int num) : m_a(num)
{
TRACE("构造函数调用了");
}
KH::shared_ptr<A> shared()
{
return shared_from_this();
}
int m_a;
~A()
{
TRACE("析构函数调用了");
}
};
void test05()
{
//构造A对象的shared_ptr
KH::shared_ptr<A> p(new A(5));
//从A对象内部获取一个shared_ptr
auto it = p->shared();
//通过此shared_ptr获取成员变量
TRACE(it->m_a);
}
可以看到enable_shared_from_this的功能实现正常
//输出
//[ /root/Code/http/test/smart_ptr_test.cpp : A : 559 ] : 构造函数调用了
//[ /root/Code/http/test/smart_ptr_test.cpp : test05 : 578 ] : 5
//[ /root/Code/http/test/smart_ptr_test.cpp : ~A : 568 ] : 析构函数调用了