RAII - std智能指针

news2024/10/13 0:37:43

std 智能指针

智能指针的选择标准

在类中使用智能指针作为成员变量。需要根据实际情况(主要是看所有权)

原始指针:

  • 所有权:该资源对象不属于我,

  • 使用前提:资源对象被释放前,我理应必然已经被释放。否则会存在风险。

unique_ptr:

  • 所有权:该对象仅仅属于我。

  • 被管理的资源对象的生命周期,取决于他所属的唯一一个引用的寿命。

shared_ptr:

  • 所有权:

    1. 该资源由多个对象共享

    2. 或者 虽然该对象仅仅属于我,但有使用 weak_ptr 的需要。

      可用于函数传参

  • 被管理的资源对象的生命周期,取决于所有引用中,最长寿的那一个。

weak_ptr:

  • 所有权:该资源对象不属于我

  • 该资源对象释放后,我仍可能不被释放时。

智能指针全家桶

相较于裸指针,智能指针对裸指针封装的初衷,是无需手动释放内存。

auto_ptr

auto_ptr 是c++ 98定义的智能指针模板,已经被C++11抛弃——支持复制运算符重载。

用于管理指针的对象,当对象过期时其析构函数将使用delete 来释放内存。

  • 复制或者赋值都会改变资源的所有权
  • 在STL容器中使用auto_ptr存在着重大风险,因为容器内的元素必须支持可复制和可赋值
  • 不支持对象数组的内存管理

unique_ptr

使用 unique_ptr 最主要的特点:对资源是独占的,防止多个智能指针指向同一个对象。

  • 更安全

    • 不支持复制运算符重载,不允许复制。
    • 支持移动语义 move,完美转发 forward
    • 因为独占,所以压根就不存在shared_ptr循环引用的问题
  • 更好的支持数组。

    std::unique_ptr<T[]>:为数组提供自动内存管理的智能指针,可用于管理动态分配的数组的内存释放。

  • 通过在析构函数中释放资源来管理对象的生命周期,来自动管理资源。

shared_ptr

资源&控制块的生命周期

被管理的资源对象的生命周期:取决于所有 shared_ptr(强引用)中最长寿的那一个。

控制块的生命周期:

  • 所有的强引用和弱引用全都析构后,控制块才会删除。
  • 因为哪怕是资源析构掉,也要满足wp查询是否资源还存在的需求。
make_shared

make_shared内存布局更加紧凑,避免内存碎片化。

若使用shared_ptr<Person> (new X) :控制块的地址与被控制资源的地址 分离。

耗费资源高

耗费资源高,不能代替 unique_ptr

  • 需要维护一个 atomic 的引用计数器,效率低,需要额外的一块管理内存,访问实际对象需要二级指针

  • 而且 deleter 使用了类型擦除技术。

线程安全

对于这个问题,通常的回答我觉得答非所问:即 sharedptr 安全又不安全:

  1. 引用计数(控制块中的计数)线程安全

    资源的生命周期是线程安全的(这就足够啦)。

  2. 访问被指向的资源不是线程安全的(请用atomic_shared_ptr

    答非所问,人家的本职工作就是解决生命周期的问题。

    这个附加要求是在此之前的所有指针的通病。要解决它就必须引入新的复杂度。

如果想要支持这个需求2

  1. 在设计资源本身时下功夫

  2. 新写一个智能指针 支持指针控制资源的多线程修改,解决这个访问资源的竞争问题

    atomic_shared_ptr

反正不要影响sharedptr的复杂度和运行时效率,重温一下 cpp语言的准则:你不需要为不需要的特性付出代价!!

The zero-overhead principle is a C++ design principle that states:

  1. You don’t pay for what you don’t use.
  2. What you do use is just as efficient as what you could reasonably write by hand.
循环引用

内存泄漏

  • 全部用 shared_ptr,可能出现循环引用之类的问题,导致内存泄露,
  • 依然需要使用不影响计数的原始指针或者 weak_ptr 来避免。
#include <iostream>
#include <string>
#include <vector>
#include <memory>
 
using namespace std;
 
class Person {
  public:
    string m_sName;
    shared_ptr<Person> m_pMother;
    shared_ptr<Person> m_pFather;
    vector<shared_ptr<Person>> m_oKids;
    // vector<weak_ptr<Person>> m_oKids; //弱指针
 
    Person (const string& sName,
            shared_ptr<Person> pMother = nullptr,
            shared_ptr<Person> pFather = nullptr)
     : m_sName(sName), m_pMother(pMother), m_pFather(pFather) {
    }

    ~Person() {
      // 由于循环引用,shared_ptr<Person> pKid pMom pDad都不会调用析构函数
      cout << "删除 " << m_sName << endl;
    }
};
 
shared_ptr<Person> initFamily (const string& sName)
{
    shared_ptr<Person> pMom(new Person(sName + "的母亲"));
    shared_ptr<Person> pDad(new Person(sName + "的父亲"));
    shared_ptr<Person> pKid(new Person(sName, pMom, pDad));
    pMom->m_oKids.push_back(pKid);
    pDad->m_oKids.push_back(pKid);
    return pKid;
}
 
int main()
{
    string sName = "张三";
    shared_ptr<Person> pPerson = initFamily(sName);
 
    cout << sName << "家存在" << endl;
    cout << "- " << sName << "被分享" << pPerson.use_count() << "次" << endl;
    cout << "- " << sName << "母亲第一个孩子的名字是:"
         << pPerson->m_pMother->m_oKids[0]->m_sName << endl;
 
    sName = "李四";
    pPerson = initFamily(sName);
    cout << sName << "家已存在" << endl;
}

运行结果

张三家存在
- 张三被分享3次
- 张三母亲第一个孩子的名字是:张三
李四家已存在

weak_ptr

意义
解决循环引用

共享指针shared_ptr指针 —— 循环引用

如果两个对象使用shared_ptr指针相互引用,并且不存在对这些对象的其他引用,若要释放这些对象及其关联的资源,则共享指针shared_ptr不会释放数据,因为每个对象的引用计数仍为1。

解决:

  • 使用普通指针,需要手动管理相关资源的释放。

  • 使用 weak_ptr

  • unique_ptr

    • 对资源是独占的,防止多个智能指针指向同一个对象。

    • 从使用场景上就不存在循环引用的可能。但是,这个需求又确确实实存在。

生命周期

弱指针weak_ptr需要共享指针shared_ptr才能创建,是共享指针shared_ptr的辅助类。

不影响指向对象的生命周期

  • 每当拥有该对象的最后一个共享指针失去其所有权时,任何弱指针weak_ptr指向的资源都会自动变为空
  • (但影响shared_ptr的控制块的生命周期)

共享但不拥有对象 的意义

  • 若使用共享指针shared_ptr指针,则其将永远不会释放对象。

  • 若使用普通指针,则可能出现指针所引用的对象不再有效,这会带来访问已释放数据的风险。

  • weak_ptr 不影响引用对象的生命周期

    ——引用的生存期可以超过了所引用的对象的生命周期。

更小的开销

你不必为不需要的特性付出代价

The zero-overhead principle is a C++ design principle that states:

  1. You don’t pay for what you don’t use.
  2. What you do use is just as efficient as what you could reasonably write by hand.
使用

弱指针weak_ptr仅提供少量操作:创建,复制和赋值一个弱指针,将弱指针转换为共享指针,检查它是否指向对象。

创建

弱指针weak_ptr指针仅提供3个构造函数

  • default构造函数
  • copy构造函数
  • 共享指针shared_ptr的构造函数。
访问

lock()会从所包含的弱指针weak_ptr中产生一个共享指针shared_ptr。其shared_ptr对象的使用计数在共享指针的生命周期内会增加。

注意,要使用弱指针,需要在表达式中插入lock()函数:

pPerson->mother->kids[0].lock()->name

而不是调用

pPerson->mother->kids[0]->name

如果无法进行此修改(例如,由于该对象的最后所有者同时释放了该对象),lock()函数会生成一个空的shared_ptr,而直接调用运算符*或->将导致未定义的行为。

检查存在

检查弱指针指向的对象是否仍然存在,则可以使用以下几种方法:

  • 接口列表 expired()

如果弱指针weak_ptr不再共享对象(空的弱指针),则返回true。

此选项等效于检查use_count()是否等于0,但可能更快。

  • 调用 use_count()

返回关联对象的所有者(共享指针shared_ptr)的所有者数量,如果返回值为0,则不再有有效的对象。

但是请注意,通常只应出于调试目的调用use_count(),因为C++标准库明确指出:“use_count()不一定有效。”

  • 通过使用相应的共享指针shared_ptr构造函数将弱指针weak_ptr显式转换为共享指针shared_ptr

如果没有有效的引用对象,则此构造方法将引发bad_weak_ptr异常。

这个异常是从std::exception派生的类的异常,其中what()会返回“ bad_weak_ptr”。

操作字典

下表为弱指针提供的所有操作。

操作结果
weak_ptr<T> wp默认构造函数;创建一个空的弱指针
weak_ptr<T> wp(sp)创建一个弱指针,共享由sp拥有的指针的所有权
weak_ptr<T> wp(wp2)创建一个弱指针,共享由wp2拥有的指针的所有权
wp.~weak_ptr()析构函数;销毁弱指针,但对拥有的对象无效
wp = wp2赋值(wp之后共享wp2的所有权,放弃先前拥有的对象的所有权)
wp = sp用共享指针sp进行赋值(wp之后共享sp的所有权,放弃先前拥有的对象的所有权)
wp.swap(wp2)交换wp和wp2的指针
swap(wp1,wp2)交换wp1和wp2的指针
wp.reset()放弃拥有对象的所有权(如果有的话),并重新初始化为空的弱指针
wp.use_count()返回共享所有者的数量(拥有对象的shared_ptr数目);如果弱指针为空,则返回0
wp.expired()返回wp是否为空(等同于wp.use_count() == 0,但可能更快)
wp.lock()返回共享指针,该共享指针共享弱指针拥有的指针的所有权(如果没有共享指针,则为空共享指针)
wp.owner_before(wp2)提供严格的弱排序和另一个弱指针
wp.owner_before(sp)通过共享指针提供严格的弱排序

原先使用shared_ptr

shared_ptr<Person> m_pMother;
shared_ptr<Person> m_pFather;
vector<shared_ptr<Person>> m_oKids;

使用weak_ptr

shared_ptr<Person> m_pMother;
shared_ptr<Person> m_pFather;
vector<weak_ptr<Person>> m_oKids; //弱指针

shared_ptr<Person> pMom(new Person(sName + "的母亲"));
shared_ptr<Person> pKid(new Person(sName, pMom, pDad));
// 尽管是vector<weak_ptr<Person>>但可以push_back分享指针
pMom->m_oKids.push_back(pKid);

std 主类型和弱引用

延伸一下:shared_ptr 是主类型,weak_ptr是其弱引用。

// 主类型 std::shared_ptr<T>
// 弱引用类型 std::weak_ptr<T>、T *

除此之外,std库中还有其它已经实现且比较常用的 主类型 与 弱引用类型

// 主类型 std::string
// 弱引用类型 std::string_view、const char *

// 主类型 std::vector<T>
// 弱引用类型 std::span<T>、(T *, size_t)

// 主类型 std::unique_ptr<T>
// 弱引用类型 T *

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2209190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LVGL快速入门】SquareLine Studio安装教程(LVGL官方工具)

一.简介与导航&#xff1a; SquareLine Studio是由LVGL官方开发的一款UI设计工具&#xff0c;采用图形化进行界面UI设计&#xff0c;轻易上手。 SquareLine Studio官方网址&#xff1a;https://squareline.io/SquareLine Studio官方文档&#xff1a;https://docs.squareline.io…

太阳能电池特性及其应用

中南民族大学-通信工程2024-大学物理下实验 目录 代码实现结果显示 &#x1f6e0;工具使用 MarsCode&#xff08;插件&#xff0c;集成在PyCharm&#xff09;&#xff1b; python编程&#xff08;豆包AI智能体&#xff09; &#x1f4bb;编程改进 此处是用「Matplotlib」来作图…

Monkey测试工具大盘点!如何选怎么用全整明白了!

什么是Monkey测试&#xff1f; 以下是官方说法&#xff1a; Monkey 测试是通过向系统发送伪随机的用户事件流&#xff08;如按键输入、触摸屏输入、手势输入等&#xff09;&#xff0c;实现对应用程序客户端的稳定性测试&#xff1b;这种随机性可以模拟真实用户的行为&#x…

理解Web3的互操作性:不同区块链的连接

随着Web3的迅速发展&#xff0c;互操作性成为区块链技术中的一个核心概念。互操作性指的是不同区块链之间能够无缝地交流和共享数据&#xff0c;从而实现更加高效和灵活的生态系统。本文将探讨Web3中互操作性的意义、面临的挑战以及未来的发展趋势。 1. 互操作性的意义 在Web…

优达学城 Generative AI 课程3:Computer Vision and Generative AI

文章目录 1 官方课程内容自述第 1 课&#xff1a;图像生成简介第 2 课&#xff1a;计算机视觉基础第 3 课&#xff1a;图像生成与生成对抗网络&#xff08;GANs&#xff09;第 4 课&#xff1a;基于 Transformer 的计算机视觉模型第 5 课&#xff1a;扩散模型第 6 课&#xff0…

利用AI大模型,增强你的DevOps!

前言 自从去年春天ChatGPT问世之后&#xff0c;互联网也掀起了拥抱AI的浪潮&#xff0c;不仅是各大头部大厂相继发布大模型产品&#xff0c;在开发者的Coding过程中也紧跟时代&#xff0c;一些热门插件也纷纷受到了开发者的青睐&#xff0c;比如GitHub Copilot的智能代码生成。…

数据结构编程实践20讲(Python版)—05二叉树

本文目录 写在前面:大“树”下好乘凉定义主要术语基本特征主要应用领域:05 二叉树Binary treeS1 说明S2 示例S3 二叉树类型(1)满二叉树(Perfect Binary Tree)(2)完全二叉树(Complete Binary Tree)(3)二叉搜索树(Binary Search Tree)(4)平衡二叉树(Balanced Bin…

Windows环境NodeJS下载配置安装运行

Windows环境NodeJS下载配置安装运行 &#xff08;1&#xff09;下载 Node.js — Run JavaScript Everywhere 安装文件。 一路傻瓜式安装。 如果安装正常&#xff0c;输入命令可显示版本号&#xff1a; &#xff08;2&#xff09;可以查询nodejs默认的后续依赖安装包位置及缓存…

稻盛和夫认为,一个领导是否值得追随,看这几点就够了

一、真正的领导者有自信&#xff0c;但不自大&#xff0c;有坚定的信念和价值观。 稻盛和夫认为&#xff0c;领导者的真正强大之处在于他们能够坚定地做正确的事情。领导者必须具备勇气和决心&#xff0c;以及坚定的信念&#xff0c;以便在面对挑战和困难时能够坚持自己的信念…

Vert.x,Web - 静态资源/模板

静态资源 Vert.x-Web带有开箱即用的处理器(StaticHandler)&#xff0c;用于处理静态Web资源(.html, .css, .js, …)&#xff0c; 因此可以非常轻松地编写静态Web服务器。 默认静态文件目录为类路径下的webroot目录&#xff0c;对于maven的项目&#xff0c;按规范放在src/main/…

BIO与NIO学习

BIO&#xff1a;同步阻塞IO&#xff0c;客户端一个连接请求&#xff08;socket&#xff09;对应一个线程。阻塞体现在: 程序在执行I/O操作时会阻塞当前线程&#xff0c;直到I/O操作完成。在线程空闲的时候也无法释放用于别的服务只能等当前绑定的客户端的消息。 BIO的代码实现 …

郑光荣参加老年春节联欢晚会团长会议现场采访

郑光荣作为北京正明圣达叫卖团的业务团长&#xff0c;他不仅在多个春节联欢晚会中展现了 自己的才华&#xff0c;还在团长会议现场接受了采访。在2024年参加了多个电视台的 春节联欢晚会录制。 郑光荣曾经参与了包括北京广播电视台、海南卫视&#xff0c;中国国际教育电视台…

c++基础-去掉空格

#include <algorithm> #include <string> #include <cctype> // 用于std::isspace std::string removeSpaces(std::string str) {str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());return str; }int main() {string str &quo…

腾讯云视立方Flutter 相关

两台手机同时运行 Demo&#xff0c;为什么看不到彼此的画面&#xff1f; 请确保两台手机在运行 Demo 时使用的是不同的 UserID&#xff0c;TRTC 不支持同一个 UserID &#xff08;除非 SDKAppID 不同&#xff09;在两个终端同时使用。 防火墙有什么限制&#xff1f; 由于 SDK…

visual studio使用ssh连接linux虚拟机运行程序

1.vs安装linux组件 2.安装后新建项目 新建后会有一个使用指南 设置网络为桥接网卡后打开虚拟机 使用vs提升的那句话安装工具 sudo apt-get install openssh-server g gdb gdbserver 重启ssh服务 sudo service ssh restart 接着进去打开ssh端口 sudo vi /etc/ssh/sshd_config …

安装rstudio-server

主要教步骤参考https://posit.co/download/rstudio-server/ 1&#xff0c;首先是linux发行版版本要求&#xff1a;符合 2&#xff0c;预装R&#xff1a;符合 3&#xff0c;安装rstudio-server 4&#xff0c;但是发现web上8787端口打不开&#xff1a; RStudio Server 可能没有在…

不会大模型不要紧!只需5分钟!你也可以微调大模型!如何快速微调Llama3.1-8B

AI浪潮席卷全球并发展至今已有近2年的时间了&#xff0c;大模型技术作为AI发展的底座和基石&#xff0c;更是作为AI从业者必须掌握的技能。但是作为非技术人员&#xff0c;相信大家也有一颗想要训练或微调一个大模型的心&#xff0c;但是苦于技术门槛太高&#xff0c;无从下手。…

Chromium 如何查找V8 引擎中JavaScript 标准内置对象

JavaScript 标准内置对象 - JavaScript | MDN (mozilla.org) 一、JavaScript 标准内置对象 本章介绍和说明了 JavaScript 中所有的标准内置对象、以及它们的方法和属性。 这里的术语“全局对象”&#xff08;或标准内置对象&#xff09;不应与 global 对象混淆。这里的“全局…

【Canvas与标牌】内凹圆角矩形排列组合标牌

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>内凹圆角矩形Draft3排列组合标牌</title><style type"…

云轴科技ZStack邀您参加迪拜GITEX 2024,10月14日不见不散

云轴科技ZStack期待在GITEX GLOBAL 2024与您相遇&#xff0c;共同探索科技的未来。 10月14日至18日&#xff0c;ZStack将携最新的云计算解决方案与AIOS智塔平台&#xff0c;亮相全球顶尖科技盛会——GITEX GLOBAL 2024 展览会&#xff0c;展位Hall 8-C20&#xff0c;向全球观众…