ROS2学习 C++中的this指针

复制代码
/*
需求:订阅发布方发布的消息,并在终端输出
流程:
1.包含头文件
2.初始化Ros2客户端
3.定义节点类
3-1.创建订阅方
3-2.处理接收到的消息
4.调用spin函数,并且传入节点对象指针
5.释放资源
*/

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
using namespace std::placeholders;
class Listener : public rclcpp::Node
{
public:
    Listener() : Node("Listener_node")
    {
        RCLCPP_INFO(this->get_logger(), "Listener_node节点创建");
        //3-1.创建订阅方
        //create_subscription(话题名,Qos服务质量,回调函数)
        /*
            模板:消息类型
            参数:
                1.话题名称:与发布方保持一致
                2.Qos服务质量:10
                3.回调函数:处理接收到的消息
                    std::bind(函数指针,类对象指针,_1表示占位符,表示接收的消息)
            返回值:订阅对象指针
        */
        subscription_ = this->create_subscription<std_msgs::msg::String>("cpp01_pubtopic", 10,
        std::bind(&Listener::subtopic_callback, this, std::placeholders::_1));
        //std::bind(...) → 绑定 "消息回调函数",意思是:当订阅器收到该话题的消息时,自动调用 subtopic_callback 函数处理消息。
    }
private:
    //3-2.处理接收到的消息,解析并输出回去
    /*
    msg 是回调函数的参数,它是 ROS2 底层在收到发布方消息后,自动创建的 std_msgs::msg::String 类型智能指针对象;当发布方发送一条消息,ROS2 会:
    在内存中创建一个 std_msgs::msg::String 对象,把发布的字符串数据存到这个对象的 data 成员里;把这个对象封装成 SharedPtr(智能指针),作为参数传给你绑定的 subtopic_callback 函数;
    你在回调函数里通过 msg->data 访问的,就是这部分临时创建的消息数据。
    */
    void subtopic_callback(const std_msgs::msg::String::SharedPtr msg) const
    {
        RCLCPP_INFO(this->get_logger(), "接收到的消息:'%s'", msg->data.c_str());
    }
    rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;
};
int main(int argc, char * argv[])
{
  //2.初始化Ros2客户端
  rclcpp::init(argc, argv);
  //4.调用spin函数,并且传入节点对象指针
  rclcpp::spin(std::make_shared<Listener>());
  //5.释放资源
  rclcpp::shutdown();
  return 0;
}

this 不是指 Listener 这个类本身,而是指当前正在创建 / 运行的 Listener 类的实例对象 (节点对象)。

1. 先理清核心概念:类 vs 实例对象

  • Listener 类(静态的) :你可以把它理解成一个 "设计图纸",定义了节点该有什么属性(比如 subscription_)、什么方法(比如构造函数、subtopic_callback),但图纸本身不能干活;
  • Listener 实例对象(动态的) :当执行 std::make_shared<Listener>() 时,会根据 "图纸" 创建一个具体的 "实物"(节点对象),这个实物才是真正能运行、能订阅消息的主体;
  • this :它是 C++ 给每个类的非静态成员函数自动提供的隐含指针,指向 "当前正在调用这个函数的实例对象"。

2. 结合代码看 this 的指向

场景 1:构造函数中的 this
复制代码
Listener() : Node("Listener_node")
{
    RCLCPP_INFO(this->get_logger(), "Listener_node节点创建");
    subscription_ = this->create_subscription<...>(...);
}

当你创建 Listener 实例(std::make_shared<Listener>())时,会调用这个构造函数:

  • 此时 this 指向的是刚创建的这个 Listener 节点对象 (比如这个对象在内存中有个地址 0x123456this 就等于这个地址);
  • this->get_logger():通过这个对象的指针,调用它继承自 rclcpp::Nodeget_logger() 方法;
  • this->create_subscription:同理,调用这个节点对象的 create_subscription 方法,创建的订阅器属于这个节点对象。
场景 2:std::bind 中的 this
复制代码
std::bind(&Listener::subtopic_callback, this, std::placeholders::_1)

这里把 this 传给 std::bind,目的是告诉 ROS2:"当触发 subtopic_callback 回调函数时,要调用这个 Listener 实例对象subtopic_callback 方法,而不是其他对象的。"

场景 3:回调函数中的 this(补充)

如果回调函数不是 const,你甚至可以通过 this 访问 / 修改节点对象的成员变量,比如:

复制代码
// 假设类里加一个成员变量
std::string last_msg_;

void subtopic_callback(const std_msgs::msg::String::SharedPtr msg)
{
    this->last_msg_ = msg->data; // this 指向当前Listener实例,把消息存到实例的成员变量里
    RCLCPP_INFO(this->get_logger(), "接收到的消息:'%s'", msg->data.c_str());
}

总结

  1. this 不是指 Listener 类(图纸),而是指 Listener 类的具体实例对象(根据图纸造出来的节点实物);
  2. 每个 Listener 实例都有自己的 this 指针,指向自身;
  3. 代码中所有的 this,最终都指向你通过 std::make_shared<Listener>() 创建的那个唯一的订阅节点对象。

简单记:类是模板,对象是实例,this 指向当前实例

先肯定你的核心结论

没错!你说的 "this 指向正在运行中的实例类(运行实例)",本质就是:

  • 静态的 Listener:是代码层面的 "模板 / 定义",不占用内存、不执行逻辑,只是规定了节点该有什么能力;
  • 动态的实例对象 :是程序运行时根据模板创建的 "实体",占用内存、有自己的状态(比如 subscription_ 订阅器),能真正和 ROS2 通信;
  • this:就是这个 "动态运行实例" 的 "自我指针"------ 它只在程序运行时存在,指向当前正在干活的那个具体实例,而非静态的类模板。

用更通俗的类比强化理解

可以把这个关系想象成:

概念 类比 特点
Listener 汽车的设计图纸 静态、只定义、不运行
Listener 实例对象 按图纸造出来的具体一辆汽车 动态、占用空间、能行驶
this 这辆汽车的 "自我定位器" 只属于这辆车,指向自己
  • 图纸(类)不会自己开,但造出来的汽车(实例)可以;
  • 汽车的 "自我定位器"(this)只指向正在开的这辆车,而不是指向图纸;
  • 就像你代码里的 this->create_subscription,是 "这辆汽车"(实例)调用了 "创建订阅器" 的功能,而不是 "图纸" 调用。

补充一个关键细节(帮你避坑)

正因为 this 指向 "运行中的实例",所以它只能在类的非静态成员函数中使用

  • 静态成员函数属于 "类"(图纸),不属于某个实例(汽车),所以静态函数里没有 this
  • 非静态成员函数(比如构造函数、subtopic_callback)属于 "实例"(汽车),所以才有 this 指针。

一、先学 "一眼区分":看函数定义时有没有 static 关键字

这是最直接的判断方法,没有任何模糊空间:

1. 非静态成员函数(默认都是)

定义时没有 static 关键字,是类的 "普通成员函数",也是你代码里最常见的类型:

复制代码
// 示例:非静态成员函数(无static)
class Listener : public rclcpp::Node
{
public:
    // 构造函数(天生非静态,永远不能加static)
    Listener() : Node("Listener_node") {}

    // 普通非静态成员函数(无static)
    void do_something() {}

private:
    // 回调函数(无static,非静态)
    void subtopic_callback(...) const {}
};
2. 静态成员函数

定义时必须加 static 关键字,属于 "类本身" 而非实例:

复制代码
class Listener : public rclcpp::Node
{
public:
    // 静态成员函数(有static)
    static void print_info() {
        RCLCPP_INFO(rclcpp::get_logger("static_logger"), "这是静态函数");
    }
};

二、结合你的 ROS2 代码:非静态成员函数有哪些?

在你写的 Listener 类中,所有函数都是非静态成员函数,典型的有:

1. 构造函数(天生非静态)
复制代码
Listener() : Node("Listener_node") { ... }
  • 构造函数的作用是创建类的实例,本身就和 "具体实例" 绑定,所以永远不能加 static,必然是非静态;
  • 这里的 this 指向正在被创建的 Listener 实例。
2. 回调函数 subtopic_callback(非静态)
复制代码
void subtopic_callback(const std_msgs::msg::String::SharedPtr msg) const { ... }
  • 虽然加了 const(表示函数不修改实例成员),但没有 static,所以还是非静态;
  • 函数里的 this->get_logger() 能正常调用,就是因为 this 指向当前实例。
3. 继承自 rclcpp::Node 的成员函数(都是非静态)

比如你用到的:

  • this->get_logger():获取当前节点实例的日志器;
  • this->create_subscription():为当前节点实例创建订阅器;
  • 还有 this->get_name()(获取节点名)、this->publish()(发布消息)等,都是非静态成员函数,必须通过实例(this)调用。

三、核心差异:静态 vs 非静态成员函数(表格更清晰)

特性 非静态成员函数 静态成员函数
关键字 static 必须加 static
所属对象 属于类的实例(每个实例独有) 属于类本身(所有实例共享)
this 指针 有(指向当前调用的实例) 无(因为不绑定具体实例)
调用方式 必须通过实例调用(obj.func()this->func() 可通过类名调用(类名::func())或实例调用
访问成员 可访问静态 / 非静态成员(通过 this 只能访问静态成员(无 this,无法访问非静态)
举个对比例子(结合你的 ROS2 场景):
复制代码
class Listener : public rclcpp::Node
{
public:
    Listener() : Node("Listener_node") {
        // 非静态函数:通过this调用(指向当前实例)
        this->get_logger(); 
        // 静态函数:通过类名调用(无需实例)
        Listener::print_info();
    }

    // 静态成员函数
    static void print_info() {
        // 错误:静态函数无this,不能调用非静态成员/函数
        // this->get_logger(); 
        // 正确:只能访问静态成员或全局内容
        RCLCPP_INFO(rclcpp::get_logger("static_logger"), "静态函数调用");
    }

private:
    // 非静态回调函数(有this)
    void subtopic_callback(...) const {
        // 正确:非静态函数有this,可调用节点的非静态方法
        RCLCPP_INFO(this->get_logger(), "收到消息");
    }

    // 非静态成员变量(实例独有)
    std::string node_info_;
    // 静态成员变量(类共享)
    static int count_;
};

// 静态成员变量需要类外初始化
int Listener::count_ = 0;
相关推荐
AI科技星2 小时前
光子的几何起源与量子本质:一个源于时空本底运动的统一模型
服务器·人工智能·线性代数·算法·机器学习
源代码•宸2 小时前
Golang原理剖析(map面试与分析)
开发语言·后端·算法·面试·职场和发展·golang·map
CodeByV2 小时前
【算法题】栈
算法
hhcccchh2 小时前
学习vue第十三天 Vue3组件深入指南:组件的艺术与科学
javascript·vue.js·学习
sprintzer2 小时前
1.6-1.15力扣数学刷题
算法·leetcode·职场和发展
jiang_bluetooth2 小时前
channel sounding基于探测序列的时延和相位差算法
算法·蓝牙测距·channel sound·gfsk·蓝牙6.0
玖釉-2 小时前
[Vulkan 学习之路] 16 - 最终章:渲染循环与同步 (Rendering & Presentation)
c++·windows·图形渲染
狗狗学不会2 小时前
Pybind11 封装 RK3588 全流程服务:Python 写逻辑,C++ 跑并发,性能起飞!
c++·人工智能·python·目标检测
DYS_房东的猫2 小时前
《 C++ 零基础入门教程》第10章:C++20 核心特性 —— 编写更现代、更优雅的 C++
java·c++·c++20