在您提供的 CPixelMatrixInfo
类的析构函数中,存在一个潜在的问题,可能导致 free(_ptr)
抛出异常。以下是详细分析:
问题根源
构造函数参数
_is_by_new
的作用:
用于指示传入的_data
是由new[]
分配(需用delete[]
释放)还是由malloc
分配(需用free
释放)。析构函数中的逻辑:
~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
。然而,这个操作发生在析构过程中,实际上没有必要。
- 当
为什么可能发生异常?
- 内存双重释放:如果多个
CPixelMatrixInfo
对象共享同一份数据(通过浅拷贝),那么当其中一个对象析构时,会触发上述逻辑,修改m_data
的删除器为free
。但当最后一个持有该数据的对象析构时,会调用free
,但如果原始数据是由new[]
分配的(但被误标记为m_isByNew=false
),则free
与delete[]
不匹配,导致未定义行为(如崩溃或异常)。 - 无效指针:如果传入的
_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助手提供,问题来源于学员提问