std::shared_ptr与std::make_unique在类函数中的使用

news2024/9/23 23:32:48

在最近学习cartographer算法的时候,发现源码中大量的使用了std::shared_ptr与std::make_unique,对于这些东西之前不是很了解,为了更好的理解源代码,因此简单学习了一下这块内容的使用,在这里简单记个笔记。

std::shared_ptr

shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。
一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。

其创建方式主要包含以下几种:
1、空shared_ptr

shared_ptr <T>  ptr;

2、使用new创建

shared_ptr<T> ptr(new T());

3、使用复制构造函数,或者重载=构造

shared_ptr<T> ptr1(new T()); 
shared_ptr<T> ptr2(ptr1);

示例:

shared_ptr<int> sp(new int(10));                //一个指向整数的shared_ptr    
assert(sp.unique());                            //现在shared_ptr是指针的唯一持有者     
shared_ptr<int> sp2 = sp;                       //第二个shared_ptr,拷贝构造函数     
assert(sp == sp2 && sp.use_count() == 2);       //两个shared_ptr相等,指向同一个对象,引用计数为2    
*sp2 = 100;                                     //使用解引用操作符修改被指对象    
assert(*sp == 100);                             //另一个shared_ptr也同时被修改     
sp.reset();                                     //停止shared_ptr的使用    
assert(!sp); 

有些东西不是完全理解,先放这儿,后面看得多了写的多了自然就理解了。

此外,与shared_ptr相反的还有unique_ptr,它的区别在于unique_ptr实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同一时间内只有一个智能指针可以指向该对象。它对于避免资源泄露(resourece leak)——例如“以new创建对象后因为发生异常而忘记调用delete”——特别有用。

std::make_unique

std::make_unique 是 C++11 标准引入的一个模板函数,用于动态分配指定类型的内存,并返回一个指向分配内存的唯一指针 (即 std::unique_ptr)。

std::make_unique 的语法如下:

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);

其中,T 是指定的类型,Args 是可变长模板参数包,用于传递给指定类型的构造函数的参数。在调用 std::make_unique 时,通过 Args 包传入构造函数的参数会被转发给类型 T 的构造函数,以生成相应的对象实例。该函数返回的指针是一个 std::unique_ptr 类型,表示一个拥有指向动态内存的所有权的对象。

为了方便理解std::shared_ptr与std::make_unique,我们在这里举个例子:

例1

#include "ros/ros.h"  
#include "std_msgs/String.h"
using namespace std;
 
class A {
public:
    //std::shared_ptr<B> pointer;
    ~A() {
        std::cout << "A was destroyed" << std::endl;
    }
    void printdata()
    {
        std::cout << "print A class data" << std::endl;
    }
};

class share_test
{
    public:
    share_test();
    ros::Subscriber data_sub;
    void init();
    private:
    std::shared_ptr<A> a;
    void dataCallback(const std_msgs::String::ConstPtr &msg);
};
share_test::share_test()
{
    ros::NodeHandle n;
	ros::NodeHandle private_nh("~");
    data_sub = n.subscribe<std_msgs::String>("/data_pub",1,&share_test::dataCallback,this);
}


void share_test::init()
{
    a = std::make_unique<A>();
    //std::shared_ptr<A> a = std::make_shared<A>();
}

void share_test::dataCallback(const std_msgs::String::ConstPtr &msg)
{
    a->printdata();
}
int main(int argc, char **argv)  
{
    ros::init(argc, argv, "share_test");  
    share_test share_test;
    share_test.init();
    ros::spin();  
    return 0;
}

在上述函数中,定义了两个类函数A以及share_test,在函数初始化中我们初始化了share_test类,同时在share_test中声明了一个shared_ptr指针:

std::shared_ptr<A> a;

注意这里创建的是一个空指针,上述创建方法中第一种。然后我们在下面的init函数中使用make_unique将其创建为动态分配的智能指针:

a = std::make_unique<A>();

此时,原来的空指针就变成了指向类A的智能指针。这里有个问题,在《C++ 的 make_unique》这篇博客中使用:

auto a = std::make_unique<A>();

的方式建立了指向A的智能指针时,a的指针类型为unique_ptr,但是这里我初始化时将其初始化为shared_ptr似乎也是一样的?不是很理解。

在完成这一步之后,就有了一个指向class A的智能指针。然后我们就可以调用class A中的相关函数,例如这里我们在订阅data_pub的回调函数中引用了A的print函数,编译运行后新开一个终端输入:

rostopic pub -1 /data_pub std_msgs/String "data: 'SSS'"

就可以在节点这边的终端显示如下输出:
在这里插入图片描述
可以看到这里打印出了class A中的打印输出。通过这种指针的方式我们可以很容易的引用一些其他类函数进行需求的解算

例2

再看一个新的例子:

#include "ros/ros.h"  
#include "std_msgs/String.h"
using namespace std;

class C
{
public:
    double input_1;
    string input_2;
    C(double data1,string data2){
        std::cout << "data1 is:" << data1 << std::endl;
        std::cout << "data2 is:" << data2 << std::endl;
        input_1 = data1;
        input_2 = data2;
    }
    void printdata()
    {
        std::cout << "print C class data" << std::endl;
        std::cout << "input_1 is:" << input_1 << std::endl;
        std::cout << "input_2 is:" << input_2 << std::endl;
    }
    ~C() {
        std::cout << "C was destroyed" << std::endl;
    }
};


class share_test
{
    public:
    share_test();
    ros::Subscriber data_sub;
    void init();
    private:
    std::shared_ptr<C> c;
    std::shared_ptr<C> c2;
    void dataCallback(const std_msgs::String::ConstPtr &msg);
};
share_test::share_test()
{
    ros::NodeHandle n;
	ros::NodeHandle private_nh("~");
    data_sub = n.subscribe<std_msgs::String>("/data_pub",1,&share_test::dataCallback,this);
}


void share_test::init()
{
    c = std::make_unique<C>(1,"abc");
    c2 = std::make_unique<C>(2,"abcd");
    //std::shared_ptr<A> a = std::make_shared<A>();
}

void share_test::dataCallback(const std_msgs::String::ConstPtr &msg)
{
    c->printdata();
    c2->printdata();
}
int main(int argc, char **argv)  
{
    ros::init(argc, argv, "share_test");  
    share_test share_test;
    share_test.init();
    ros::spin();  
    return 0;
}

这里与上面的例子略有不同,这里的calss C定义了一个初始化函数,需要传递两个参数,同时在它的print函数中会打印这两个参数。而在class share_test中创建了两个指针:

    std::shared_ptr<C> c;
    std::shared_ptr<C> c2;

随后我们再次使用make_unique创建了两个指向class C的智能指针:

    c = std::make_unique<C>(1,"abc");
    c2 = std::make_unique<C>(2,"abcd");

编译运行这个程序,终端会显示如下:
在这里插入图片描述
可以看到这里输出了4条打印,这是因为share_test::init()函数初始化了两个智能指针,class C这个类作为模板类被使用了两次。

而后,我们再次触发一下subscriber回调函数,可以显示打印如下:
在这里插入图片描述
到这里执行了两次打印调用:

c->printdata();
c2->printdata();

这两个智能指针虽然是指向了同一个模板类,但是由于我们在创建的时候传参是不一样的,因此它们的打印结果也是不一样的,这个就是模板类与智能指针的好处了,通常我们只需要定一一个类函数,然后通过这样一个make_unique的方式创建一个智能指针,就可以使用它里面的一些函数,同时,由于它的指针是独享的,因此我虽然两个指针指向的是同一个类,但是由于类初始化时传递的参数不一致,最后得到的结果也是不一致的,这就极大的便利了一些重复性类函数的使用,例如在cartographer中,使用的子图的概念,就是通过类函数与指针的形式实现的。

参考:
std::shared_ptr 详解
shared_ptr(共享指针)使用总结
C++11新特性之十三:std::make_unique和std::make_shared
C++ 的 make_unique(含 C++ 代码示例)

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

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

相关文章

【热门话题】深入浅出:npm常用命令详解与实践

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 标题&#xff1a;深入浅出&#xff1a;npm常用命令详解与实践引言一、npm基本概…

打流仪/网络测试仪这个市场还能怎么卷?

#喝了点&#xff0c;码点字# 以下为个人观点&#xff0c;看看就好&#xff0c;如有冒犯&#xff0c;私信删稿 都有哪些厂商在做打流仪/网络测试仪 -洋品牌&#xff1a;思博伦/Viavi-Spirent&#xff0c;是德/Keysight-Ixia&#xff0c;信雅纳/Lecroy-Xena&#xff0c; -国产…

睿尔曼超轻量仿人机械臂之-灵巧手动作编写及程序调用

一、灵巧手动作编写 1.连接设备 2. 运动控制 3. 参数设置 4 动作库使用 本软件可以设置灵巧手内部第 1-第 13 套动作序列数据&#xff0c;每套动作序列最多能有 8 步 分解动作&#xff0c;每一步分解动作的手指角度、运动速度、力度以及等待时间都可以单独设置。 步骤数&…

QT_day2:2024/3/21

作业1&#xff1a;使用QT完成一个登录界面 要求&#xff1a; 1. 需要使用Ui界面文件进行界面设计 2. ui界面上的组件相关设置&#xff0c;通过代码实现 3. 需要添加适当的动图 源代码&#xff1a; #include "widget.h" #include "ui_widget.h"Widget…

力扣由浅至深 每日一题.06 删除有序数组中的重复项

希望我们都能对抗生活的苦难&#xff0c;在乌云周围突破阴霾积极的生活 —— 24.3.16 删除有序数组中的重复项 提示 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元…

贝尔曼方程【Bellman Equation】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 文章目录 强化学习笔记一、状态值函数贝尔曼方程二、贝尔曼方程的向量形式三、动作值…

Windows系统部署GoLand结合内网穿透实现SSH远程Linux服务器开发调试

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法|MySQL| ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-HIOuHATnug3qMHzx {font-family:"trebuchet ms",verdana,arial,sans-serif;f…

【Vue3遇见的问题】创建vue3的项目使用vscode打开后项目的app.vue里面存在爆红

出现的问题 直接上上问题:问题的图片如下: 解决方法 解决效果 补充 因为vetur的插件禁用了 所以需要一个新插件来 这里发现的官网推荐的插件 也就是volar 他两是一样的

嵌入式软件面试-linux-中高级问题

Linux系统启动过程&#xff1a; BIOS自检并加载引导程序。引导程序&#xff08;如GRUB&#xff09;加载Linux内核到内存。内核初始化硬件&#xff0c;加载驱动&#xff0c;建立内存管理。加载init进程&#xff08;PID为1&#xff09;&#xff0c;通常是systemd或SysVinit。init…

Redis监控工具

Redis 是一种 NoSQL 数据库系统&#xff0c;以其速度、性能和灵活的数据结构而闻名。Redis 在许多领域都表现出色&#xff0c;包括缓存、会话管理、游戏、排行榜、实时分析、地理空间、叫车、聊天/消息、媒体流和发布/订阅应用程序。Redis 数据集完全存储在内存中&#xff0c;这…

深度学习——数据预处理

一、数据预处理 为了能用深度学习来解决现实世界的问题&#xff0c;我们经常从预处理原始数据开始&#xff0c; 而不是从那些准备好的张量格式数据开始。 在Python中常用的数据分析工具中&#xff0c;我们通常使用pandas软件包。 像庞大的Python生态系统中的许多其他扩展包一样…

es 集群核心概念以及实践

节点概念&#xff1a; 节点是一个Elasticsearch的实例 本质上就是一个JAVA进程一台机器上可以运行多个Elasticsearch进程&#xff0c;但是生产环境一般建议一台机器上只运行一个Elasticsearch实例 每一个节点都有名字&#xff0c;通过配置文件配置&#xff0c;或者启动时候 -…

JVM学习-JVM简介以及其内部结构

目录 1.什么是JVM 2.JVM、JRE、JDK、JavaSE、JavaEE之间的联系 3.JVM的内部结构 4.各部分的作用 4.1 类加载器&#xff08;附类加载过程导航&#xff09; 4.2 方法区 4.3 堆&#xff08;附常量池、运行时常量池、串池的介绍导航&#xff09; ​ 4.4 虚拟机栈&am…

Android14 - Framework- Configuration的创建和更新

本文描述从启动一个新进程的Activity起&#xff0c;Framwork层Configuration的创建和传导过程。 首先&#xff0c;我们知道所有的Window容器都继承于WindowContainer&#xff0c;而WindowContainer本身是ConfigurationContainer的子类。于此同时&#xff0c;WindowProcessContr…

高级数据结构 <AVL树>

本文已收录至《数据结构(C/C语言)》专栏&#xff01; 作者&#xff1a;ARMCSKGT 目录 前言正文AVL树的性质AVL树的定义AVL树的插入函数左单旋右单旋右左双旋左右双旋 检验AVL树的合法性关于AVL树 最后 前言 前面我们学习了二叉树&#xff0c;普通的二叉树没有任何特殊性质&…

数据结构面试常见问题之Insert or Merge

&#x1f600;前言 本文将讨论如何区分插入排序和归并排序两种排序算法。我们将通过判断序列的有序性来确定使用哪种算法进行排序。具体而言&#xff0c;我们将介绍判断插入排序和归并排序的方法&#xff0c;并讨论最小和最大的能区分两种算法的序列长度。 &#x1f3e0;个人主…

pycorrector检测OCR错字实践

参考&#xff1a;https://github.com/shibing624/pycorrector/tree/master/examples/macbert stopwords.txt 添加专业停用词&#xff0c;避免错误 设置自定义词典&#xff0c;避免将正确的词错误检测成错误的词 from pycorrector import Corrector m Corrector() m.set_cus…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.4 会计凭证处理

2.4.1 会计凭证处理的基本概念 会计凭证是企业经济业务在会计上的反映&#xff0c;它是用会计语言表达的一种单据。 典型生产企业的财务凭证创建方式&#xff1a; 企业在实施SAP的过程中&#xff0c;大部分凭证都是自动生成的。要保证这些凭证能准确地生成&#xff0c;必须要满…

mysql 数据库 基本介绍

一 数据 &#xff08;一&#xff09;数据是什么 描述事物的符号记录 包括数字&#xff0c;文字、图形、图像、声音、档案记录气 以“记录”形式按统一的格式进行存储 &#xff08;二&#xff09;数据的分类 1&#xff0c;结构化的数据 即有固定格式和有限长度的数据。例…

hcip复习总结1

OSI----------- 定义了数据的产生标准 。 7 层 应用 ------- 表示 会话 传输 -----Telnet - 23 ssh---22 http---80 https-443 TCP ---- 传输控制卋议。是一种面向连接的可靠的传输卋议。 UDP---- 用户数据报卋议。是一种非面向连接的丌可靠传输卋议。 保证可靠性&…