C++ 智能指针

unique_ptr<T,Deleter>

  1. 同时只能有一个unique_ptr指向创建的对象
  2. unique_ptr不可拷贝和拷贝赋值,可以移动和移动赋值
  3. 可以存放数组,存放数组时可以使用 [] 运算符访问数组元素
  4. 可以直接传给shared_ptr的构造函数,从而转换为shared_ptr
  5. 派生类的unique_ptr可以赋值给基类的unique_ptr(需要保证删除器相同)。 unique_ptr没有专用的类型转换函数,基类的unique_ptr不能赋值给派生类的unique_ptr。
  6. 可以用unique_ptr::release函数获取对象,并将对象与unique_ptr解除绑定。通过解除绑定,再重新创建新类型的unique_ptr,可以完成unique_ptr类型转换。
  7. 可以自定义删除器,删除器属于unique_ptr类型的一部分,不同删除器的unique_ptr不能相互赋值。
auto createUniquePointer()
{
    constexpr auto deleter = [](int* p) { delete p; };
    using DeleterType = decltype(deleter);
    auto up = std::unique_ptr<int, DeleterType>(new int, deleter);
    return up;
}

shared_ptr<T>

  1. 多个shared_ptr同时指向同一个对象
  2. 支持拷贝赋值和移动赋值
  3. C++17开始支持数组,同时支持 [] 与索引访问数组元素
  4. 可以使用函数std::dynamic_pointer_cast、static_pointer_cast进行转换
  5. 一旦对象放入shared_ptr,则与shared_ptr始终绑定,不可以解绑
  6. 支持自定义删除器,删除器是存放在控制块中,删除器类型与shared_ptr的类型无关,不同删除器的shared_ptr可以相互赋值。
  7. shared_ptr内部使用了原子计数,所以如果可以使用移动尽量使用移动语义,可以减少原子变量的操作
  8. 使用make_shared可以让对象和控制块内存分配在同一块内存上,可以减少一次内存分配,提高效率。

实现

shared_ptr使用控制块实现,share_ptr内部会有两个指针,一个是当前类型数据对象,一个是控制块的指针。

控制块

控制块通过继承实现,内部使用了虚函数,shared_ptr内部存储的是基类的指针,与实际的控制块类型无关。控制块的类型与删除器deleter相关,shared_ptr并没有使用控制器类型,而是用了基类, 这就是为什么不同类型删除器的shared_ptr可以相互赋值。

ptr

控制块内部会存储一个分配内存的指针Ref_count::ptr,用于存储分配内存时的指针,这个指针和shared_ptr<T>::ptr大部分情况下时一样的,但也有不同的时候,主要有两种情况下不一样:

  1. 当类型T有多重继承,类型转换后不同基类的地址不相同,Ref_count::ptr始终指向分配内存时的指针,而shared_ptr<T>::ptr会根据实际基类类型改变。
  2. 使用shared_ptr<T>( const shared_ptr<Y>& ref, T* ptr )重新构造的智能指针,这里构造的shared_ptr<T>和传入的 ref 使用的是相同的控制块,实际的类型可以是对象内部的任意数据。

strong_ref

strong_ref是shared_ptr的引用计数,当前strong_ref变为0时会调用对象的析构函数,但不一定会释放对象的内存,

如果shared_ptr创建时使用的是new 构建的对象或使用了自定义删除器则会释放对象的内存。

如果shared_ptr是通过make_shared创建的,此时对象和控制块是同时分配在同一块内存上,这种情况是否释放内存还需要看weak_ref的值。

weak_ref

weak_ref是weak_ptr的引用计数,当strong_ref和weak_ref都为0时则会释放控制块的内存

weak_ptr<T>

  1. weak_ptr内部变量和shared_ptr是相同的,但不可以直接访问对应的指针和对象。
  2. weak_ptr是shared_ptr的弱引用,会影响weak_ref引用计数,weak_ptr不会影响对象的生命周期,但是会影响控制块的生命周期。
  3. 可以通过expired函数检测当前对象是否已经释放
  4. 可以通过lock函数转换为shared_ptr,如果对象已经释放,返回null。
  5. 通过shared_ptr<T>(weak_ptr<T> wp)构造shared_ptr时,如果weak_ptr已经expired则会抛出异常

enable_shared_from_this

  1. enable_shared_from_this可以让对象获取对应的shared_ptr
  2. enable_shared_from_this内部会存储一个weak_ptr指针,当调用shared_from_this时,会使用weak_ptr构造一个shared_ptr并返回
  3. 如果对象没有放在shared_ptr中,调用shared_from_this时会抛出异常,因为内部的weak_ptr已经expired,内部通过通过shared_ptr(weak_ptr)构造shared_ptr时会抛出异常。
  4. 可以通过函数weak_from_this来判断当前对象是否在shared_ptr中。
  5. 构造函数调用shared_from_this,因为此时对象还没有放入shared_ptr,weak_ptr为expired,会导致抛出异常。
  6. shared_ptr构造函数内部会识别类型是否继承至enable_shared_from_this,如果是继承至enable_shared_from_this则会设置内部的weak_ptr。

enable_shared_from_this只需要基类继承,派生类无需再次继承,需要使用public继承方式。如果基类和派生类都继承了enable_shared_from_this会导致shared_ptr识别类型失败,而无法使用shared_from_this。

示例

std::cout << "------------- enable_shared_from_this-----------------" << std::endl;
struct A : public std::enable_shared_from_this<A>
{
    virtual ~A() = default;
    std::weak_ptr<A> wpA() { return weak_from_this(); }
    std::shared_ptr<A> spA() { return shared_from_this(); }
};
struct B : public std::enable_shared_from_this<B>, public A
{
    std::weak_ptr<B> wpB() { return std::enable_shared_from_this<B>::weak_from_this();}
    std::shared_ptr<B> spB() { return std::enable_shared_from_this<B>::shared_from_this();}
};
struct C : public A
{
    std::weak_ptr<C> wpC() { return  std::dynamic_pointer_cast<C>(weak_from_this().lock());}
    std::shared_ptr<C> spC() { return std::dynamic_pointer_cast<C>(shared_from_this());}
};

std::shared_ptr<B> spB = std::shared_ptr<B>(new B);
std::cout << "B, wpB weak_from_this expired: " << spB->wpB().expired() << std::endl;
std::cout << "B, wpA weak_from_this expired: " << spB->wpA().expired() << std::endl;

std::shared_ptr<C> spC = std::shared_ptr<C>(new C);
std::cout << "C, wpC weak_from_this expired: " << spC->wpC().expired() << std::endl;
std::cout << "C, wpA weak_from_this expired: " << spC->wpA().expired() << std::endl;

运行结果

reference

std::unique_ptr - cppreference.com

std::shared_ptr - cppreference.com

std::weak_ptr - cppreference.com

std::enable_shared_from_this - cppreference.com

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针是一种用于管理动态分配的内存资源的工具。C++中提供了多种类型的智能指针,其中包括shared_ptr、unique_ptr和weak_ptr。这些智能指针都位于头文件<memory>中。 其中,shared_ptr是一种引用计数智能指针,它允许多个智能指针共享同一个对象。shared_ptr通过对对象的引用计数来管理内存的释放,当引用计数为0时,即没有智能指针指向该对象时,对象会被自动释放。使用shared_ptr需要包含头文件<memory>,并通过new关键字创建动态分配的对象并将其交给shared_ptr进行管理。 另一种智能指针是unique_ptr,它是一种独占型智能指针,只能有一个智能指针拥有对对象的所有权。当unique_ptr对象被销毁时,它所管理的对象也会被自动释放。unique_ptr提供了更高效的内存管理方式,因为它不需要进行引用计数。使用unique_ptr也需要包含头文件<memory>,并使用new关键字创建动态分配的对象并将其交给unique_ptr进行管理。 除了shared_ptr和unique_ptr,还有其他类型的智能指针,如weak_ptr和scoped_ptr。weak_ptr是一种弱引用智能指针,它用于解决shared_ptr可能出现的循环引用问题。scoped_ptr是一种简单的智能指针,它只能在创建时初始化,并且不能进行复制和赋值操作。scoped_ptr在其所属的作用域结束时自动释放所管理的对象。 总结起来,C++智能指针是一种用于管理动态分配的内存资源的工具,包括shared_ptr、unique_ptr、weak_ptr和scoped_ptr等类型。它们提供了一种更安全、更高效的内存管理方式,避免了手动释放内存的麻烦。要使用这些智能指针,需要包含头文件<memory>,并通过new关键字创建动态分配的对象并将其交给相应的智能指针进行管理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C++ -- 智能指针C++11与boost库的智能指针及其使用)](https://blog.csdn.net/xu1105775448/article/details/80625936)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [C++智能指针的底层实现原理](https://blog.csdn.net/ArtAndLife/article/details/120793343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值