You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ROS C++订阅器段错误原因及Subscriber构建方式差异咨询

ROS订阅器段错误问题分析与解答

我编写了一个订阅字符串类型消息话题的ROS订阅器,第一版程序运行时提示Segmentation fault (core dumped)且无法执行,修改后第二版程序可正常运行。

版本一代码

#include "ros/ros.h"
#include "std_msgs/String.h"

class NameSubscriber
{
private:
    ros::Subscriber sub;
    ros::NodeHandle nh;
public:
    NameSubscriber(std::string _topic, int queue_size)
    : nh(ros::NodeHandle()),
    sub(nh.subscribe(_topic, queue_size, &NameSubscriber::Callback, this))
{
}
    
    void Callback(const std_msgs::String::ConstPtr& msg)
    {
        ROS_INFO("### Success get publisher");
        ROS_INFO("I get the message is: %s, at: %f", msg->data.c_str(),ros::Time::now().toSec());
    } 
};

int main(int argc, char **argv)
{
    ros::init(argc, argv, "name_s");
    NameSubscriber name_sub("sub_Anthony",100);
    ros::spin();
    return 0;
}

版本二修改部分

public:
    NameSubscriber(std::string _topic, int queue_size)
    : nh()
    {
        sub = nh.subscribe(_topic, queue_size, &NameSubscriber::Callback, this);
    }
    
...

问题解答

1. 导致Segmentation fault (core dumped)错误的原因

核心原因是C++类成员的初始化顺序规则:类成员的初始化顺序严格按照它们在类中声明的顺序执行,与构造函数初始化列表中的顺序无关。

在版本一中,类的私有成员声明顺序是ros::Subscriber sub在前,ros::NodeHandle nh在后。虽然初始化列表里先写了nh(ros::NodeHandle()),再写sub(...),但实际执行时会先初始化sub,此时nh还未完成构造。用一个未构造完成的NodeHandle对象调用subscribe()方法,会访问非法内存地址,最终触发段错误。

2. 两种ros::Subscriber对象构建方式的差异

两种方式分别是初始化列表初始化和构造函数体内赋值,核心差异如下:

  • 执行时机不同:
    • 初始化列表初始化:在类成员的默认构造阶段之前执行,直接构造目标对象。
    • 构造函数体内赋值:所有类成员完成默认初始化后,再对sub进行赋值操作。
  • 依赖对象的状态不同:
    • 版本一中的初始化列表方式,sub初始化时依赖的nh尚未构造完成,属于非法访问。
    • 版本二中的体内赋值方式,nh已经在初始化列表中完成默认构造(nh()),此时用合法的NodeHandle对象创建Subscriber,不会出现内存访问问题。
  • 语义区别:
    • 初始化列表是对象的构造语义,直接完成对象的初始化,适合不可赋值的成员(如const成员、引用)。
    • 体内赋值是赋值语义,先默认构造对象再赋值,仅适用于支持赋值操作的类型(ros::Subscriber支持赋值,因此可用)。

内容的提问来源于stack exchange,提问作者Anthony Chen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 19:22:42