类构造函数中初始化tf::MessageFilter对象触发std::bad_alloc错误,改用指针则正常的代码差异分析
为什么第一种
PoseDrawer实现会触发std::bad_alloc,而指针版本能正常运行? 咱们先直接说核心差异,再拆解背后的原因:
核心差异
- 第一个实现中,
tf_filter_是类的成员对象,在构造函数的初始化列表中直接完成初始化; - 第二个实现中,
tf_filter_是指针类型的成员,在构造函数函数体内部通过new动态分配并初始化。
为什么第一个版本会抛出std::bad_alloc?
问题出在C++类成员的初始化顺序规则上:C++类的成员是按照它们在类中声明的顺序来初始化的,和构造函数初始化列表里写的顺序完全无关!
咱们看第一个版本的成员声明顺序:
private: message_filters::Subscriber<geometry_msgs::PointStamped> point_sub_; tf::TransformListener tf_; tf::MessageFilter<geometry_msgs::PointStamped> tf_filter_; ros::NodeHandle n_; std::string target_frame_;
实际初始化顺序是:point_sub_ → tf_ → tf_filter_ → n_ → target_frame_
而你在构造函数初始化列表里写的顺序是:
PoseDrawer() : tf_(), target_frame_("turtle1"), tf_filter_(point_sub_, tf_, target_frame_, 10)
看起来你是先初始化tf_和target_frame_再处理tf_filter_,但C++会严格按声明顺序执行:
- 先默认初始化
point_sub_ - 接着初始化
tf_ - 直接开始初始化
tf_filter_——这时候就出问题了:n_还没初始化(因为它在tf_filter_之后声明),你后续才会在构造函数体里调用point_sub_.subscribe(n_, ...),所以此时point_sub_是未完成订阅的无效状态;target_frame_也还没初始化(同样因为声明顺序靠后),把一个未初始化的std::string传给tf_filter_的构造函数,会导致内部内存操作出现非法行为,最终触发std::bad_alloc(本质是非法内存访问引发的分配失败)。
为什么第二个版本能正常运行?
把tf_filter_改成指针后,初始化逻辑完全变了:
- 构造函数初始化阶段,只会处理非指针成员:
point_sub_、tf_、n_、target_frame_都会按声明顺序完成默认/指定初始化; - 进入构造函数体后,你先调用
point_sub_.subscribe(n_, ...)——此时n_已经完全初始化,point_sub_能正常完成订阅; - 最后通过
new创建tf_filter_,此时传入的point_sub_(已订阅)、tf_(已初始化)、target_frame_(已赋值)都是完全有效的状态,自然不会出现内存分配错误。
说白了就是:第一个版本踩了C++初始化顺序的坑,用指针延迟初始化就完美避开了这个问题。
内容的提问来源于stack exchange,提问作者b3hn4m
相关产品推荐
相关产品推荐

