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
相关产品推荐
相关产品推荐

