You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++14自定义结构体场景智能指针赋值与C风格转换问题

问题背景

基于C++14开发程序时,需要分配内存获取驱动相关信息,流程为:调用可返回信息存储位置指针的接口后,绑定自定义结构体数组到对应内存地址即可访问目标数据。原有基于new/delete的实现可正常运行,为规避内存泄漏风险,计划改用智能指针实现自动内存管理。

原有可运行实现代码

首先定义两个业务相关结构体:

// 用于获取指针信息的自定义结构体
typedef struct tag_SelectData
{                               
    short iCount;               // 可选数据的数量
    short iSize;                // 单个可选数据的字节长度
    int *lpData;                // 可选数据数组指针
                                // *应用必须为该成员分配缓冲区,并将缓冲区地址赋值给该参数
                                // 如果lpData为0,函数仅输出iCount和iSize字段
} SELECTDATA;

// 存储目标驱动信息的结构体
typedef struct tag_MediaTypeRange_4
{
    DWORD dwVersion;     // 结构体版本,必须为4
    DWORD dwSize;        // 结构体大小
    long iMaxWid;        // 最大宽度
    long iMaxHig;        // 最大高度
    long iMinWid;        // 最小宽度
    long iMinHig;        // 最小高度
    long iTypeID;        // 媒体类型ID
} MEDIATYPERANGE_4;

原始业务逻辑代码:

/***  原有业务实现  ***/
SELECTDATA selectData; 
selectData.iCount = 0;
selectData.iSize = 0;
selectData.lpData = NULL;

GetSelectDataRange(CMD_MEDIATYPE, &selectData);  // 调用接口获取内存指针

MEDIATYPERANGE_4 *arr = new MEDIATYPERANGE_4[selectData.iCount]; // 分配数组内存
arr = (MEDIATYPERANGE_4 *)selectData.lpData;  // 将接口返回的指针赋值给数组指针

// 遍历数组输出结果
for (size_t i = 0; i < selectData.iCount; i++)  
{
  std::cout << arr[i].iMaxWid << std::endl;
  std::cout << arr[i].iMinWid << std::endl;
  std::cout << arr[i].iMaxHig << std::endl;
  std::cout << arr[i].iMinHig << std::endl;
}

改造遇到的问题

改造为智能指针实现时,在指针赋值环节编译失败:
首先尝试创建持有自定义结构体数组的shared_ptr实例:

// 创建自定义结构体类型的智能指针
std::shared_ptr<MEDIALAYOUTRANGE_4[]> mediaArr(new MEDIALAYOUTRANGE_4[selectData.iCount]());

已知后续可通过mediaArr.get()[i]的方式访问数组元素,但创建智能指针时仅完成了自身内存分配,无法将selectData.lpData指向的内存地址绑定给智能指针管理。尝试直接用C风格转换赋值均无法通过编译:

  • 构造场景报错:无合适构造函数
  • 赋值场景报错:=运算符无匹配操作数

错误写法示例:

// 构造时报错
std::shared_ptr<MEDIALAYOUTRANGE_4 []> mediaArr = (MEDIATYPERANGE_4 *)selectData.lpData; 
// 赋值时报错
mediaArr = (MEDIATYPERANGE_4 *)selectData.lpData; 
正确实现方式

首先明确两个基础问题:

  1. 原始裸指针实现本身就存在内存泄漏:new出来的数组指针马上被selectData.lpData覆盖,申请的内存既没有被使用也没有被释放。
  2. 不要直接把接口返回的lpData交给智能指针默认析构:这块内存是驱动接口返回的,不是通过new申请的,默认用delete[]释放大概率触发程序崩溃。
  3. 你代码里的类型名拼写错误:MEDIALAYOUTRANGE_4和实际结构体名MEDIATYPERANGE_4不一致,这也是编译报错的原因之一。

根据接口的内存管理规则,分两种场景实现:

场景1:lpData指向的内存由驱动自行管理,不需要应用释放

这种情况不需要额外申请内存,也不需要给返回的指针套智能指针,直接转换指针遍历访问即可,最后按驱动文档要求调用对应释放接口(如果有要求):

SELECTDATA selectData{};
GetSelectDataRange(CMD_MEDIATYPE, &selectData);
// 直接转换接口返回的指针即可,不需要额外new内存
MEDIATYPERANGE_4* arr = reinterpret_cast<MEDIATYPERANGE_4*>(selectData.lpData);

for (size_t i = 0; i < selectData.iCount; i++)
{
    std::cout << arr[i].iMaxWid << '\n';
    std::cout << arr[i].iMinWid << '\n';
    std::cout << arr[i].iMaxHig << '\n';
    std::cout << arr[i].iMinHig << '\n';
}

场景2:lpData需要应用自行分配内存,接口把数据写入应用分配的缓冲区

这才是结构体注释里描述的标准用法:注释明确说明应用必须为lpData成员分配缓冲区,你之前的调用流程写反了,正确流程是先获取需要的内存大小,分配缓冲区后把地址赋给lpData,再调用接口让驱动把数据写入你分配的内存。这种场景适合用智能指针管理自申请的内存:

SELECTDATA selectData{};
// 第一次调用传空lpData,获取需要的元素数量和单元素大小
GetSelectDataRange(CMD_MEDIATYPE, &selectData);

// 校验单元素大小和结构体大小是否匹配,避免内存越界
if (selectData.iSize != sizeof(MEDIATYPERANGE_4) || selectData.iCount <= 0) {
    // 对应错误处理逻辑
    return;
}

// 用智能指针管理自己分配的数组内存,出作用域自动调用delete[]释放
// 如果不需要共享所有权,更推荐用std::unique_ptr<MEDIATYPERANGE_4[]>,开销更小且C++14原生支持下标访问
std::shared_ptr<MEDIATYPERANGE_4[]> mediaArr(new MEDIATYPERANGE_4[selectData.iCount]());
// 把分配到的内存地址赋给lpData,供接口写入数据
selectData.lpData = reinterpret_cast<int*>(mediaArr.get());
// 第二次调用接口,让驱动把数据写入我们分配的缓冲区
GetSelectDataRange(CMD_MEDIATYPE, &selectData);

// C++14下shared_ptr<T[]>没有实现operator[],需要通过get()拿到裸指针再访问下标
for (size_t i = 0; i < selectData.iCount; i++)
{
    std::cout << mediaArr.get()[i].iMaxWid << '\n';
    std::cout << mediaArr.get()[i].iMinWid << '\n';
    std::cout << mediaArr.get()[i].iMaxHig << '\n';
    std::cout << mediaArr.get()[i].iMinHig << '\n';
}

特殊场景:需要接管外部分配的内存

如果确实需要把驱动返回的内存交给shared_ptr管理,必须自定义和内存分配规则匹配的删除器,绝对不能用默认删除器。比如驱动要求用DriverFree接口释放传入的内存,写法如下:

// 自定义删除器,调用驱动提供的释放接口
auto driverDeleter = [](MEDIATYPERANGE_4* p) { 
    // 调用驱动对应的内存释放接口,例如 DriverFree(p);
};
std::shared_ptr<MEDIATYPERANGE_4[]> mediaArr(
    reinterpret_cast<MEDIATYPERANGE_4*>(selectData.lpData), 
    driverDeleter
);

内容的提问来源于stack exchange,提问作者rockstiff

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 04:12:23