使用std::sort排序需深拷贝的结构体时触发内存异常的解决咨询
问题描述
定义了继承自gsk_oid的oidX结构体,以及包含oidX*成员的oidPair结构体,创建oidPair数组后调用std::sort排序时触发内存异常,第三次调用Oid_less比较函数时,right参数引用的oidPair包含未初始化的oidX指针。
相关代码如下:
基础结构体定义
typedef unsigned int gsk_uint32; typedef struct _gsk_oid { int count; gsk_uint32 * elements; } gsk_oid; struct oidX : public gsk_oid { oidX(); // 默认构造函数 oidX(int countX, gsk_uint32 first, ...); // 从整数列表构造 oidX(const char* oidString); // 从点分符号构造 oidX(const gsk_oid *inOID); // 从gsk_oid构造 ~oidX(); // 析构函数 };
oidPair结构体定义
struct oidPair { oidX *TheOID; const char *Descript; oidPair(oidX *inputOID, const char *inputDescript); ~oidPair(); };
数组初始化与排序调用
oidPair testOIDs[4] = { oidPair(new oidX("2.23.140.1.2.3"), "Certificates issued in accordance with the CA/Browser Forum's Baseline Requirements - Individual identity asserted"), oidPair(new oidX("2.23.140.1.1"), "Extended Validation (EV) guidelines certificate policy"), oidPair(new oidX("1.3.6.1.4.1.4146.1.20"), "Organization validation certificate policy"), oidPair(new oidX("2.23.140.1.2.2"), "Certificates issued in accordance with the CA/Browser Forum's Baseline Requirements - Organization identity asserted") }; std::sort(testOIDs, testOIDs+4, &Oid_less);
比较函数实现
int DecodeOID::Oid_cmp(const gsk_oid *left, const gsk_oid *right) { int cnt = std::min(left->count, right->count); // 取两个向量中较短的长度 int ret = memcmp(left->elements, right->elements, cnt*4); // 比较元素 if ( 0 != ret ) return ret; // 若不相等则返回结果 return left->count - right->count; // 否则较长的视为更大 } bool DecodeOID::Oid_less(const oidPair &left, const oidPair &right) { const gsk_oid *l = left.TheOID; const gsk_oid *r = right.TheOID; int ret = Oid_cmp(l, r); return ret < 0; }
问题根源
std::sort在排序过程中需要对数组元素进行拷贝、移动或交换操作,但oidPair仅定义了构造函数和析构函数,没有实现拷贝构造函数、拷贝赋值运算符、移动构造函数、移动赋值运算符。编译器生成的默认版本是浅拷贝,会直接复制TheOID指针,导致多个oidPair对象指向同一个oidX实例,析构时重复释放内存;同时,排序过程中创建临时对象时,默认操作可能导致指针未初始化,最终触发内存访问异常。
解决方案
有两种可行方案,推荐优先使用智能指针方案,避免手动内存管理错误。
方案一:手动实现三/五法则
为oidPair实现完整的拷贝、移动操作,确保指针成员的正确管理:
struct oidPair { oidX *TheOID; const char *Descript; // 构造函数 oidPair(oidX *inputOID, const char *inputDescript) : TheOID(inputOID), Descript(inputDescript) {} // 拷贝构造:深拷贝oidX实例 oidPair(const oidPair& other) : TheOID(new oidX(other.TheOID)), Descript(other.Descript) {} // 移动构造:接管原对象的指针,避免深拷贝 oidPair(oidPair&& other) noexcept : TheOID(other.TheOID), Descript(other.Descript) { other.TheOID = nullptr; // 原对象放弃指针所有权 } // 拷贝赋值运算符 oidPair& operator=(const oidPair& other) { if (this != &other) { // 避免自我赋值 delete TheOID; // 释放当前指针 TheOID = new oidX(other.TheOID); // 深拷贝新对象 Descript = other.Descript; } return *this; } // 移动赋值运算符 oidPair& operator=(oidPair&& other) noexcept { if (this != &other) { delete TheOID; TheOID = other.TheOID; Descript = other.Descript; other.TheOID = nullptr; } return *this; } // 析构函数 ~oidPair() { delete TheOID; } };
注意:需确保oidX的拷贝构造函数正确深拷贝elements数组,示例如下:
oidX::oidX(const gsk_oid *inOID) { count = inOID->count; elements = new gsk_uint32[count]; std::memcpy(elements, inOID->elements, count * sizeof(gsk_uint32)); } oidX::oidX(const oidX& other) { count = other.count; elements = new gsk_uint32[count]; std::memcpy(elements, other.elements, count * sizeof(gsk_uint32)); }
方案二:使用智能指针(推荐)
用std::unique_ptr替换裸指针,让智能指针自动管理内存,无需手动实现拷贝/移动函数:
#include <memory> struct oidPair { std::unique_ptr<oidX> TheOID; const char *Descript; // 构造函数:接收移动后的unique_ptr oidPair(std::unique_ptr<oidX> inputOID, const char *inputDescript) : TheOID(std::move(inputOID)), Descript(inputDescript) {} // 无需手动实现析构、拷贝/移动函数,编译器自动生成正确逻辑 }; // 数组初始化写法调整 oidPair testOIDs[4] = { oidPair(std::make_unique<oidX>("2.23.140.1.2.3"), "Certificates issued in accordance with the CA/Browser Forum's Baseline Requirements - Individual identity asserted"), oidPair(std::make_unique<oidX>("2.23.140.1.1"), "Extended Validation (EV) guidelines certificate policy"), oidPair(std::make_unique<oidX>("1.3.6.1.4.1.4146.1.20"), "Organization validation certificate policy"), oidPair(std::make_unique<oidX>("2.23.140.1.2.2"), "Certificates issued in accordance with the CA/Browser Forum's Baseline Requirements - Organization identity asserted") };
std::unique_ptr是独占所有权的智能指针,排序过程中会自动移动指针,避免浅拷贝问题,同时自动释放内存,彻底消除手动管理内存的风险。
内容的提问来源于stack exchange,提问作者Charles
相关产品推荐
相关产品推荐

