当前位置: 首页 > 科技观察

手握智能教鞭,你学会了吗?

时间:2023-03-21 19:14:50 科技观察

本文转载自微信公众号《程序喵大师》,作者程序喵。转载本文请联系程序大师喵公众号。估计大家在面试的时候应该都会问到C++11的shared_ptr是怎么实现的。引用计数的概念大家应该都能回答出来,但是如果要手写一个shared_ptr,能不能写出来呢?最近自己写了一个简单的shared_ptr,分享到这里。首先定义一个负责引用计数的类:classSharedCount{public:SharedCount():count_{1}{}voidadd(){++count_;}voidminus(){--count_;}intget()const{返回count_;}私有:std::atomiccount_;};然后是SharedPtr类,首先在构造函数中创建SharedCount对象:{nullptr},ref_count_{newSharedCount}{}private:T*ptr_;SharedCount*ref_count_;};通过构造函数创建的SharedPtr引用计数必须为1。如何实现析构函数?无非就是将引用计数减1,如果最后引用计数减为0,则释放所有指针:}private:voidclean(){if(ref_count_){ref_count_->minus();if(ref_count_->get()==0){if(ptr_)deleteptr_;删除ref_count_;}}}};然后是智能指针的关键部分是在复制构造和复制赋值时将引用计数加1:templateclassSharedPtr{public:SharedPtr(constSharedPtr&p){this->ptr_=p.ptr_;this->ref_count_=p.ref_count_;ref_count_->add();}SharedPtr&operator=(constSharedPtr&p){clean();这个->ptr_=p.ptr_;this->ref_count_=p.ref_count_;ref_count_->add();返回*这个;}};处理拷贝语义,还需要处理移动语义,即实现移动构造和移动赋值函数:templateclassSharedPtr{public:SharedPtr(SharedPtr&&p){this->ref_count_=p.ref_count_;p.ptr_=nullptr;p.ref_count_=nullptr;}SharedPtr&operator=(SharedPtr&&p){clean();this->ptr_=p.ptr_;this->ref_count_=p.ref_count_;p.ptr_=nullptr;p.ref_count_=nullptr;返回*这个;}};在移动语义中,引用计数保持不变,并清除原始参数中的指针关于共享指针,这里已经实现了基本的逻辑,但是还需要增加获取裸指针,获取引用计数等接口:这样一个完整的智能指针已经基本实现了,运行看看:structA{A(){std::cout<<"A()\n";}~A(){std::cout<<"~A()\n";}};voidtest_simple_shared(){A*a=newA;SharedPtrptr(a);{std::cout<classSharedPtr{public:intuse_count(){returnref_count_->get();}T*get()const{返回ptr_;}T*operator->()const{returnptr_;}T&operator*()const{return*ptr_;}operatorbool()const{returnptr_;}私有:T*ptr_;SharedCount*ref_count_;};基本的shared_ptr写完了,写点有意思的,不知道你用过这些指针转换函数吗:structA{A(){std::cout<<"A()\n";}~A(){std::cout<<"~A()\n";}};voidtest_simple_shared(){A*a=newA;SharedPtrptr(a);{std::cout<b=ptr;std::cout<c=ptr;std::cout<d=std::move(b);std::cout<std::shared_ptrconst_pointer_cast(conststd::shared_ptr&r)noexcept;templatestd::shared_ptrdynamic_pointer_cast(conststd::shared_ptr&r)noexcept;templatestd::shared_ptrreinterpret_pointer_cast(conststd::shared_ptr&r)noexcept;我假设大家已经知道这些函数的作用了,那我们直接研究一下它们的实现:{this->ptr_=ptr;this->ref_count_=p.ref_count_;ref_count_->add();}T*ptr_;SharedCount*ref_count_;};templateSharedPtrstatic_pointer_cast(constSharedPtr&p){T*ptr=static_cast(p.get());returnSharedPtr(p,ptr);}SharedPtr和SharedPtr不是一个类,所以上面的代码会稍微有点问题,p不能访问它的私有成员变量ref_count,那怎么解决呢?朋友:模板classSharedPtr{public:template朋友类SharedPtr;};上面的代码还是有问题的,因为SharedPtr(constSharedPtr&p,T*ptr)是private的,static_pointer_cast是访问不到的,有两种方式,一种是变成public,一种是变成friend,这里Friend比较合理,添加好友后的代码如下:templateclassSharedPtr{public:templatefriendclassSharedPtr;templatefriendSharedPtrstatic_pointer_cast(constSharedPtr&p);private:templateSharedPtr(constSharedPtr&p,T*ptr){this->ptr_=ptr;this->ref_count_=p.ref_count_;ref_count_->add();}T*ptr_;SharedCount*ref_count_;};templateSharedPtrstatic_pointer_cast(constSharedPtr&p){T*ptr=static_cast(p.get());returnSharedPtr(p,ptr);}再次测试:structA{A(){std::cout<<"A()\n";}~A(){std::cout<<"~A()\n";}};结构B:A{B(){std::cout<<"B()\n";}~B(){std::cout<<"~B()\n";}};voidtest_cast_shared(){B*a=newB;SharedPtrptr(a);{std::cout<b=static_pointer_cast(ptr);std::cout<c=ptr;std::cout<d=ptr;std::cout<ptr_=p.ptr_;p.ptr_=nullptr;}UniquePtr&operator=(UniquePtr&&p){clean();this->ptr_=p.ptr_;p.ptr_=nullptr;返回}T*get()const{返回ptr_;}T*operator->()const{returnptr_;}T&operator*()const{return*ptr_;}operatorbool()const{returnptr_;}~UniquePtr(){干净();}private:voidclean(){if(ptr_)deleteptr_;}T*ptr_;};重点其实只有这两个删除:templateclassUniquePtr{public:UniquePtr(constUniquePtr&p)=删除;UniquePtr&operator=(constUniquePtr&p)=delete;};到目前为止,已经实现了一个简单的shared_ptr和unique_ptr。希望对大家有所帮助。完整代码见这里:https://github.com/chengxumiaodaren/cpp-learning/blob/master/src/test_shared_ptr.cc