cpp 智能指针

一、auto_ptr

auto_ptr 是 C++98 标准中引入的智能指针,用于自动管理动态分配的对象。然而,它在语义上有一些缺陷,因此在 C++11 标准中被弃用,并由 unique_ptr 取代。

问题与缺陷:

1、所有权模型auto_ptr 采用了“独占所有权”模型,即一个 auto_ptr 独占其指向的对象。当你将一个 auto_ptr 赋值给另一个 auto_ptr 时,所有权会从原 auto_ptr 转移到新的 auto_ptr,原 auto_ptr 会变成空。这种所有权转移行为常常导致意外的错误。

2、不支持数组auto_ptr 只能用于指向单个对象,而不能用于指向对象数组。尝试使用 auto_ptr 管理动态分配的数组会导致 delete 而不是 delete[] 被调用,从而引发未定义行为。

3、与标准库容器的兼容性问题:由于 auto_ptr 的复制语义(所有权转移),它不能与标准库容器(如 std::vector)安全地一起使用。当容器尝试复制或重新排列其元素时,可能会意外地删除或移动元素。

由于上述问题,C++11 标准中引入了 unique_ptr,它提供了与 auto_ptr 类似的独占所有权特性,但解决了 auto_ptr 的缺陷。unique_ptr 支持动态数组和移动语义,并且可以与 STL 容器和算法完美配合。

二、unique_ptr

unique_ptr通过指针占用并独占一个对象,并在unique_ptr离开作用域时释放该对象。

主要特点:

1、唯一所有权:一个 unique_ptr 指向的对象只能被一个 unique_ptr 所拥有。

2、自动删除:当 unique_ptr 被销毁时(例如,离开作用域时),它所指向的对象也会被自动删除。

3、禁止复制unique_ptr 不支持拷贝构造和赋值操作,以防止多个 unique_ptr 指向同一个对象。

4、支持移动unique_ptr 支持移动构造函数和移动赋值操作符,允许将所有权从一个 unique_ptr 转移到另一个 unique_ptr

三、shared_ptr

std::unique_ptr 相比,它支持多个指针共享对同一对象的所有权,而不仅限于单一所有权。这种特性使得 std::shared_ptr 在需要多个所有者管理同一对象的情况下非常有用。

主要特点:

1、引用计数std::shared_ptr 使用引用计数来跟踪有多少个 shared_ptr 指向同一个对象。每次创建或复制 shared_ptr 时,引用计数会增加;每次 shared_ptr 被销毁或重置时,引用计数会减少。当引用计数为零时,shared_ptr 会自动销毁对象并释放内存。

2、自动资源释放std::shared_ptr 采用了RAII(Resource Acquisition Is Initialization)技术,它在对象生命周期结束时自动释放相关资源,无需手动调用 delete

3、共享所有权std::shared_ptr 支持多个 shared_ptr 对象共享同一块内存资源。当所有的 shared_ptr 对象都被销毁或重置时,内存才会被释放。

4、可定制删除器std::shared_ptr 允许指定一个删除器(deleter)函数来代替默认的 delete 操作,这样可以实现自定义的资源释放策略。

5、可拷贝std::shared_ptr 可以被拷贝,每次拷贝都会增加引用计数。当最后一个 shared_ptr 被销毁时,内存会被释放。

6、异常安全std::shared_ptr 的析构函数和删除器会在异常抛出时正常工作,避免资源泄露。

初始化方式:

std::shared_ptr 可以通过多种方式进行初始化,包括但不限于:

1、裸指针直接初始化:直接传入一个 new 表达式的结果给 shared_ptr 的构造函数。

2、拷贝构造:通过拷贝另一个 shared_ptr 对象来初始化新的 shared_ptr 对象。

3、移动构造:C++11 引入了移动语义,shared_ptr 也支持通过移动构造来初始化新的 shared_ptr 对象,这通常用于函数返回或传递所有权时。

4、std::make_shared:这是一个工厂函数,用于更方便地创建 shared_ptr 对象。与直接使用 new 表达式相比,std::make_shared 可以更高效地分配内存,因为它可以在单次内存分配中同时分配对象和控制块。

常用成员函数:

std::shared_ptr 提供了一些常用的成员函数,以便进行各种操作:

  • get():返回 shared_ptr 中保存的裸指针。
  • reset():不带参数时,若智能指针是唯一指向该对象的指针,则释放并置空;若智能指针不是唯一指向该对象的指针,则引用计数减少1,同时将智能指针置空。带参数时,若智能指针是唯一指向对象的指针,则释放并指向新的对象;若智能指针不是唯一的指针,则只减少引用计数,并指向新的对象。
  • use_count():返回 shared_ptr 的强引用计数。
  • unique():若 use_count() 为1,返回 true,否则返回 false

注意事项:

1、尽量避免将一个裸指针传递给 std::shared_ptr 的构造函数,常用的替代手法是使用 std::make_shared

2、不要将 this 指针返回给 shared_ptr。当希望将 this 指针托管给 shared_ptr 时,类需要继承自 std::enable_shared_from_this,并且从 shared_from_this() 中获得 shared_ptr 指针。

3、不要使用相同的原始指针作为实参来创建多个 shared_ptr 对象,这会导致多重控制块的创建和对象的多次析构。

4、避免循环引用,即一组 shared_ptr 相互引用,可能会导致内存泄漏,因为它们的引用计数永远不会变为 0,从而无法释放内存。所以引入了 weak_ptr 来解决这个问题。

四、weak_ptr

weak_ptr 是一种弱引用指针,用于解决 shared_ptr 可能导致的循环引用问题。它可以观测到 shared_ptr 所管理的对象,但不会增加引用计数,不影响对象的生命周期。当最后一个 shared_ptr 被销毁时,即使还有 weak_ptr 指向该对象,对象也会被释放。

主要特点:

1、不增加引用计数weak_ptr 指向一个 shared_ptr 管理的对象时,不会增加该对象的引用计数。

2、解决循环引用:在对象间存在相互引用时,使用 weak_ptr 可以避免循环引用导致的内存泄漏。

3、观察对象状态:通过 weak_ptr,可以观察 shared_ptr 管理的对象的状态,如使用 use_count() 获取引用计数,使用 expired() 检查对象是否已被释放。

基本用法:

1、初始化weak_ptr 通常由一个 shared_ptr 或另一个 weak_ptr 初始化。

std::shared_ptr<int> sp = std::make_shared<int>(10);  
std::weak_ptr<int> wp(sp);

2、访问对象:由于 weak_ptr 不拥有对象的所有权,因此不能直接通过 * 或 -> 操作符访问对象。需要先通过 lock() 方法将其转换为 shared_ptr,然后才能访问对象。

if (std::shared_ptr<int> sp2 = wp.lock()) {  
    std::cout << *sp2 << std::endl;  
} else {  
    std::cout << "wp指向的对象已被释放" << std::endl;  
}

3、检查对象状态

  • use_count():返回与 weak_ptr 指向的 shared_ptr 关联的引用计数(但请注意,这个值可能会立即变化,因为其他线程可能正在操作 shared_ptr)。
  • expired():检查 weak_ptr 指向的 shared_ptr 是否已被销毁。如果返回 true,则表示对象已被释放,此时 lock() 将返回一个空的 shared_ptr

注意事项:
1、weak_ptr 不应单独使用,它总是与 shared_ptr 配合使用。

2、由于 weak_ptr 不增加引用计数,因此它不能单独保证对象的生命周期。当所有 shared_ptr 被销毁时,即使还有 weak_ptr 指向该对象,对象也会被释放。

3、在多线程环境中使用 weak_ptr 时,需要特别注意线程安全问题。由于 use_count() 返回的值可能会立即变化,因此它不能用于多线程中的同步或协调。

觉得有帮助的话,打赏一下呗。。

           

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值