ROS NodeHandle.subscribe调用派生类方法出现链接错误求助
问题分析与解决:ROS中派生类方法作为订阅回调的链接错误
问题场景
在Ubuntu18.04 + ROS Melodic + C++14环境下,尝试基于基类Scan_Matching实现多个派生类(如ICP),通过主程序选择实例并将派生类的get_transform方法作为ROS话题订阅的回调函数。代码如下:
class Scan_Matching { public: void get_transform(const sensor_msgs::PointCloud2ConstPtr& cloud_msg); }; class ICP : public Scan_Matching { public: void get_transform(const sensor_msgs::PointCloud2ConstPtr& cloud_msg); }; int main(){ ... Scan_Matching* scan_matching; scan_matching = new ICP(...); ros::Subscriber sub = nh.subscribe(topic, 100, &Scan_Matching::get_transform, scan_matching); ... }
编译时出现链接错误:
[ 14%] Linking CXX executable /home/mike/documents/autonomous_sim/devel/lib/mike_av_stack/localization CMakeFiles/mike_av_stack_node.dir/scripts/localization/localization.cpp.o: In function `main': localization.cpp:(.text.startup+0x718): undefined reference to `Scan_Matching::get_transform(boost::shared_ptr<sensor_msgs::PointCloud2_<std::allocator<void> > const> const&)' collect2: error: ld returned 1 exit status mike_av_stack/CMakeFiles/mike_av_stack_node.dir/build.make:392: recipe for target '/home/mike/documents/autonomous_sim/devel/lib/mike_av_stack/localization' failed make[2]: *** [/home/mike/documents/autonomous_sim/devel/lib/mike_av_stack/localization] Error 1 CMakeFiles/Makefile2:2493: recipe for target 'mike_av_stack/CMakeFiles/mike_av_stack_node.dir/all' failed make[1]: *** [mike_av_stack/CMakeFiles/mike_av_stack_node.dir/all] Error 2 Makefile:145: recipe for target 'all' failed make: *** [all] Error 2 Invoking "make -j12 -l12" failed
测试全局回调函数可正常编译,排除参数类型不匹配问题。
错误原因
- 基类方法无实现且未声明为虚函数:基类
Scan_Matching的get_transform只有声明没有定义,链接器找不到该函数的实现,导致链接错误。 - 无法实现多态调用:即使基类有方法实现,由于未将
get_transform声明为虚函数,ROS订阅回调会调用基类的方法而非派生类的重载方法,无法达到不同算法实例化的需求。
解决方法
方案1:将基类方法声明为纯虚函数(推荐,适合无默认实现场景)
将基类的get_transform声明为纯虚函数,使基类成为抽象类,强制派生类必须实现该方法,同时确保多态调用生效:
class Scan_Matching { public: // 纯虚函数,派生类必须实现 virtual void get_transform(const sensor_msgs::PointCloud2ConstPtr& cloud_msg) = 0; }; class ICP : public Scan_Matching { public: // 派生类实现具体逻辑 void get_transform(const sensor_msgs::PointCloud2ConstPtr& cloud_msg) override { // ICP算法的点云匹配逻辑 } };
方案2:将基类方法声明为虚函数并提供默认实现
如果基类需要默认的匹配逻辑,可将方法声明为虚函数并提供实现:
class Scan_Matching { public: // 虚函数,带默认实现 virtual void get_transform(const sensor_msgs::PointCloud2ConstPtr& cloud_msg) { // 基类默认的点云匹配逻辑 } }; class ICP : public Scan_Matching { public: // 派生类重载并实现自己的逻辑 void get_transform(const sensor_msgs::PointCloud2ConstPtr& cloud_msg) override { // ICP算法的点云匹配逻辑 } };
主程序订阅部分无需修改
原主程序中的订阅代码保持不变即可,因为虚函数机制会自动根据指针指向的实际派生类实例调用对应的方法:
int main(){ ... Scan_Matching* scan_matching; scan_matching = new ICP(...); ros::Subscriber sub = nh.subscribe(topic, 100, &Scan_Matching::get_transform, scan_matching); ... }
补充说明
- 使用
override关键字显式标记派生类的重载方法,可帮助编译器检查是否正确重载了基类的虚函数,避免因签名不一致导致的隐性错误。 - ROS的订阅机制支持类成员函数作为回调,只要确保函数签名匹配,且通过虚函数实现多态,即可正确调用派生类的方法。
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

