重温C++顺序容器vector增长的内存分配,并记一次使用栈变量地址构造动态指针导致程序崩溃的stupid

教训:

ROS的sensor_msgs::LaserScanPtr与sensor_msgs::LaserScanConstPtr都是boost库的动态指针,不是一般指针。自己在栈中创建的LaserScan变量不该用来构造sensor_msgs::LaserScanPtr或sensor_msgs::LaserScanConstPtr,因为对栈变量的“释放”将导致程序崩溃!


namespace sensor_msgs
{
template <class ContainerAllocator>
struct LaserScan_
{
  typedef LaserScan_<ContainerAllocator> Type;

  LaserScan_()
    : header()
    , angle_min(0.0)
    , angle_max(0.0)
    , angle_increment(0.0)
    , time_increment(0.0)
    , scan_time(0.0)
    , range_min(0.0)
    , range_max(0.0)
    , ranges()
    , intensities()  {
    }
  LaserScan_(const ContainerAllocator& _alloc)
    : header(_alloc)
    , angle_min(0.0)
    , angle_max(0.0)
    , angle_increment(0.0)
    , time_increment(0.0)
    , scan_time(0.0)
    , range_min(0.0)
    , range_max(0.0)
    , ranges(_alloc)
    , intensities(_alloc)  {
  (void)_alloc;
    }

}; // struct LaserScan_

typedef ::sensor_msgs::LaserScan_<std::allocator<void> > LaserScan;

typedef boost::shared_ptr< ::sensor_msgs::LaserScan > LaserScanPtr;
typedef boost::shared_ptr< ::sensor_msgs::LaserScan const> LaserScanConstPtr;

示例代码:

#include<iostream>
#include<string>
#include<vector>
#include"unistd.h"
using namespace std;
class MyClass{
    private:
        string field;
    public:
        static int n_construct;
        static int n_destruct;
        MyClass(string var):field(var){
            n_construct++;
        };
        ~MyClass(){
            cout<<"~MyClass("<<field<<")"<<endl;
            n_destruct++;
        }
        string getfield(){
            return field;
        }
};
void destruct_MyClass(MyClass *pobj){
    cout<<"self define destruct MyClass("<<pobj->getfield()<<")"<<endl;
    delete pobj;    // ~MyClass(xxxx)
}
int MyClass::n_construct(0);
int MyClass::n_destruct(0);
void func(std::shared_ptr<MyClass> pobj){
    cout<<"dandling MyClass("<<pobj->getfield()<<")"<<endl;
    return;
}
int main(int argc,char **argv){
    vector<MyClass> arr;
    cout<<"arr.capacity() "<<arr.capacity()<<" &arr "<<&arr<<endl;
                                    // 0 vector容器初始capacity()=0
    // arr.resize(3);
    arr.push_back(MyClass("haha"));         // 1、完成push_back()函数传参后析构局部实参
    cout<<"arr.capacity() "<<arr.capacity()<<" &arr "<<&arr<<endl;
                                    // 1
    arr.push_back(MyClass("heihei"));       // 2、容器扩容,析构~MyClass("haha"),并直接内存层面拷贝MyClass("haha")到新空间,但arr虚存地址不变
    cout<<"arr.capacity() "<<arr.capacity()<<" &arr "<<&arr<<endl;
                                    // 2
    arr.push_back(MyClass("ouou"));         // 3、扩容,逆压栈顺序析构~MyClass("heihei")与~MyClass("haha")
    cout<<"arr.capacity() "<<arr.capacity()<<" &arr "<<&arr<<endl;
                                    // 4
    arr.push_back(MyClass("lala"));         // 4、不需扩容
    cout<<"arr.capacity() "<<arr.capacity()<<" &arr "<<&arr<<endl;
                                    // 4
    // 5、容器扩容时首先直接内存拷贝到新空间(不必重新构造),然后析构掉旧空间的实例
    cout<<"construct total "<<MyClass::n_construct<<" instances"<<endl;
    cout<<"destruct total "<<MyClass::n_destruct<<" instances"<<endl;
    cout<<"sleep 1s"<<endl;
    sleep(1);
    for(MyClass &iobj:arr){
        cout<<"arr[0].getfield() "<<arr[0].getfield()<<" &arr "<<&arr<<endl;

        // func( (std::shared_ptr<MyClass>)&iobj );
        /**
         * @brief 
         * 临时实参(动态指针)指向栈变量,传参后临时实参+形参 引用计数等于2,临时实参动态指针首先析构,
         * 形参然后析构导致引用计数归零,直接析构arr[0]即~MyClass("haha")
         */

        std::shared_ptr<MyClass> pobj(&iobj,destruct_MyClass);
        func(pobj);
        cout<<"sleep 1s"<<endl;
        sleep(1);
        /**
         * @brief 
         * 局部实参(动态指针)指向栈变量,传参后局部实参+形参 引用计数等于2,形参动态指针首先析构,
         * 局部实参pobj然后析构导致引用计数归零,直接析构arr[0]即~MyClass("haha")
         * 破坏arr
         */
    }
}
/**
 * @brief 
 * 输出:
 * sleep 1s
    arr[0].getfield() haha &arr 0x7ff7bfefeff8
    dandling MyClass(haha)              // 析构形参动态指针
    sleep 1s
    self define destruct MyClass(haha)  // 局部实参动态指针pobj析构,然后引用计数归零再析构栈上实例MyClass(haha),释放栈空间?
    ~MyClass(haha)

    arr[0].getfield() haha &arr 0x7ff7bfefeff8  // 虽然释放了栈空间,但arr[0]还是能将这段内存数据解释出MyClass("haha")实例
    dandling MyClass(heihei)
    sleep 1s
    self define destruct MyClass(heihei)
    ~MyClass(heihei)
                                        // 但为什么在遍历到arr[2]也就是第三个元素时才报错?
    main(82642,0x100098600) malloc: *** error for object 0x600002608018: pointer being freed was not allocated
    main(82642,0x100098600) malloc: *** set a breakpoint in malloc_error_break to debug
 * 总结:
    不要用一个原始指针初始化多个shared_ptr
    不要在函数实参中创建shared_ptr。
    通过shared_from_this()返回this指针。
    要避免循环引用
 */

工程代码:

for(const sensor_msgs::LaserScan &iscan:scans){
    sensor_msgs::LaserScan *pscan(new sensor_msgs::LaserScan(iscan));     // copy construct
    pextract_reflectBoard->scan_callback((const sensor_msgs::LaserScanConstPtr)pscan,ob_reflectBoard);
    // 释放堆变量而不是栈变量,尤其是栈容器,try &iscan
}

遵循动态指针指向堆变量的原则,并牢记sensor_msgs::LaserScanPtr是动态指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值