ZBLOG

// 像素矩阵信息(图像数据信息) class CPixelMatrixInfo { // NOTE: 图像数据的内存布局遵循 OpenCV 的 cv::Mat、IplImage 等的格式 // TODO: 未处理内存对齐问题,所以若 width 不符合内存对齐要求,会发生错乱 public: // uchar * data () const { return m_data.get() ; } // 数据首地址 int ...

在您提供的 CPixelMatrixInfo 类的析构函数中,存在一个潜在的问题,可能导致 free(_ptr) 抛出异常。以下是详细分析:


问题根源

  1. 构造函数参数 _is_by_new 的作用
    用于指示传入的 _data 是由 new[] 分配(需用 delete[] 释放)还是由 malloc 分配(需用 free 释放)。

  2. 析构函数中的逻辑

    ~CPixelMatrixInfo() {
       if (m_data && !m_isByNew) {
           m_data = std::shared_ptr<uchar[]>(m_data.get(),
               [] (uchar *_ptr) {
                   free(_ptr);   // 可能发生异常
               });
       }
    }
    
    • m_data 非空且不是由 new[] 分配时(即 m_isByNew=false),尝试重新构造一个 std::shared_ptr,并指定自定义删除器(使用 free 释放内存)。
    • 但这里存在严重问题:重新赋值 m_data 会导致原 std::shared_ptr<uchar[]>(默认删除器为 delete[])被替换,而新构造的 shared_ptr 的删除器是 free。然而,这个操作发生在析构过程中,实际上没有必要。
  3. 为什么可能发生异常?

    • 内存双重释放:如果多个 CPixelMatrixInfo 对象共享同一份数据(通过浅拷贝),那么当其中一个对象析构时,会触发上述逻辑,修改 m_data 的删除器为 free。但当最后一个持有该数据的对象析构时,会调用 free,但如果原始数据是由 new[] 分配的(但被误标记为 m_isByNew=false),则 freedelete[] 不匹配,导致未定义行为(如崩溃或异常)。
    • 无效指针:如果传入的 _data 不是由动态内存分配(例如栈地址或已释放的指针),那么 free(_ptr) 会直接导致异常。
    • 多线程问题:如果在多线程环境中修改共享数据,可能引发竞争条件。

修复方案

方案1:正确设置删除器(推荐)

在构造函数中直接根据 _is_by_new 设置正确的删除器,避免在析构函数中临时修改:

explicit CPixelMatrixInfo(uchar *_data, int _width, int _height, int _channels, 
                          int _bit_depth = 8, int _step = 0, bool _is_by_new = false) 
    : m_width(_width), m_height(_height), m_channels(_channels), 
      m_bitDepth(_bit_depth), m_step(_step), m_isByNew(_is_by_new)
{
    if (_data) {
        // 根据分配方式设置正确的删除器
        if (_is_by_new) {
            m_data = std::shared_ptr<uchar[]>(_data, [](uchar *ptr) { delete[] ptr; });
        } else {
            m_data = std::shared_ptr<uchar[]>(_data, [](uchar *ptr) { free(ptr); });
        }
    } else {
        // 自主分配内存:默认用 new[]
        size_t size = _height * (_step > 0 ? _step : _width * _channels * (_bit_depth / 8));
        m_data = std::shared_ptr<uchar[]>(new uchar[size], [](uchar *ptr) { delete[] ptr; });
        m_isByNew = true;
    }

    // 若未指定步长,则计算默认步长(宽度 * 通道数 * (位深/8))
    if (m_step <= 0) {
        m_step = m_width * m_channels * (m_bitDepth / 8);
    }
}

然后移除析构函数中的逻辑(直接使用默认析构):

~CPixelMatrixInfo() = default;

方案2:完全移除析构函数中的操作

如果无法修改构造函数,至少移除析构函数中的危险操作:

~CPixelMatrixInfo() = default;

但前提是构造函数已经正确设置了删除器(目前代码并未体现)。


总结

  • 根本原因:在析构函数中临时修改共享指针的删除器可能导致双重释放或资源管理混乱。
  • 建议:在构造函数中一次性设置正确的删除器(根据 _is_by_new),并依赖 std::shared_ptr 的自动管理机制,无需自定义析构函数。
  • 额外注意:确保所有共享该数据的对象都使用相同的删除器逻辑(浅拷贝时共享指针本身会同步删除器)。


    内容由零声教学AI助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=24315

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?