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

C++数组存储PointCloudSubscriber实例引发内存崩溃问题求助

数组创建ROS订阅器实例导致内存崩溃的原因与解决方法

我来帮你拆解这个问题,其实根源出在默认赋值操作导致的ROS订阅器回调绑定失效,和boost::shared_ptr关系不大,具体分析如下:

问题核心原因

你用数组创建PointCloudSubscriber实例时,执行了两步有隐患的操作:

  1. 先通过默认构造函数创建了两个空的pcs[0]和pcs[1]对象;
  2. 再用临时对象PointCloudSubscriber(nh, ...)赋值给数组元素。

关键问题就在这里:当你创建临时对象时,带参构造函数里执行了node_handle.subscribe(..., &PointCloudSubscriber::callback, this)——这里的this指向的是临时对象本身。赋值完成后,临时对象会被立即销毁,但ROS的订阅器还持有这个已经被释放的this指针。当点云消息到来触发回调时,程序会尝试访问一块已经被回收的内存,直接引发double free or corruption这类内存错误。

而单独创建实例时,pc1和pc2是直接在栈上构造的,this指针指向的是持续存在的栈对象,回调绑定始终有效,所以能正常运行。

另外,你没有自定义PointCloudSubscriber的赋值运算符,编译器生成的默认赋值会浅拷贝ros::Subscriber对象。虽然ros::Subscriber本身支持拷贝,但拷贝后的订阅器会共享原临时对象的回调绑定,临时对象销毁后就留下了悬空的this指针。

解决建议

按推荐程度排序,有几种可靠的修复方式:

方法1:直接在数组/容器中构造对象(避免默认构造+赋值)

这是最简洁的方案,直接跳过默认构造+赋值的步骤,在初始化时就完成对象构造:

// 方式1:用初始化列表(C++11及以上支持)
PointCloudSubscriber pcs[] = {
    PointCloudSubscriber(nh, "/kinect1/sd/points", 1),
    PointCloudSubscriber(nh, "/kinect2/sd/points", 1)
};

// 方式2:用vector动态构造(更灵活,适合数量不确定的场景)
std::vector<PointCloudSubscriber> pcs;
pcs.emplace_back(nh, "/kinect1/sd/points", 1);
pcs.emplace_back(nh, "/kinect2/sd/points", 1);

这样每个PointCloudSubscriber都是直接构造在数组/vector中,this指针指向的是有效存在的对象,回调绑定不会失效。

方法2:禁用拷贝和赋值操作,从源头避免错误

为了防止不小心触发错误的赋值行为,可以在类中禁用拷贝构造和赋值运算符(C++11及以上支持):

class PointCloudSubscriber {
private:
    ros::Subscriber subscriber;
    void callback(const sensor_msgs::PointCloud2ConstPtr& msg);
public:
    std::string topic;
    sensor_msgs::PointCloud2 point_cloud;
    
    PointCloudSubscriber(){};
    PointCloudSubscriber(ros::NodeHandle& node_handle, std::string topic_name, int queue_size);
    
    // 禁用拷贝和赋值,编译器会直接拦截错误操作
    PointCloudSubscriber(const PointCloudSubscriber&) = delete;
    PointCloudSubscriber& operator=(const PointCloudSubscriber&) = delete;
};

这样如果再尝试用赋值的方式创建实例,编译器会直接报错,从根源上避免内存问题。

方法3:自定义赋值运算符,手动转移订阅关系(不推荐)

如果一定要保留默认构造+赋值的方式,可以自定义移动赋值运算符,手动转移订阅资源并重新绑定回调:

PointCloudSubscriber& PointCloudSubscriber::operator=(PointCloudSubscriber&& other) noexcept {
    if (this != &other) {
        // 先退订当前的订阅
        subscriber.shutdown();
        // 转移其他对象的资源
        topic = std::move(other.topic);
        point_cloud = std::move(other.point_cloud);
        // 重新订阅并绑定当前对象的this指针
        subscriber = other.node_handle.subscribe<sensor_msgs::PointCloud2>(
            topic, 1, &PointCloudSubscriber::callback, this
        );
        // 清空原对象的订阅器
        other.subscriber.shutdown();
    }
    return *this;
}

这种方法需要额外处理ROS订阅器的生命周期,复杂度较高,不如前两种方案可靠。

补充说明

你提到的boost::shared_ptr并不是问题的根源,崩溃是因为回调访问了悬空的对象内存,而非shared_ptr的非法使用。sensor_msgs::PointCloud2ConstPtr是ROS封装的安全智能指针,本身使用没有问题,只是当对象被销毁后,point_cloud的赋值操作会访问无效内存。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:31:47