p指针->AddRef() 和 p指针->Relase()

本文详细介绍了D3D中接口对象的管理方法,包括引用计数的原理、AddRef与Release的使用时机,以及在不同场景下如何正确操作接口指针以避免内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 D3D是COM组件,它在服务进程中运行,而不在当前的客户进程中。在DX组件运行过程中,要创建一系列接口对象,如CreateDevice()返回接口指针,这些接口及其占用内存什么时候释放,要通过“引用计数”的技术来解决。AddRef()给这个接口指针的计数加1,而Release()会将之减1.一旦减到0,表示没有客户使用了,相关的接口就释放了。 由此可知,每次调用Rlease()后,并不一定会释放内存,而是当引用计数归0时释放内存。
 
  这样,对接口指针的使用,就像维护堆栈的平衡一样,要仔细,而且按照某种约定规则使用。
 
  但平时D3D编程中,怎么不用AddRef()呢?这是由于一个接口指针,如ID3DDevice或VertexBuf指针,都是D3DXCreate出来的,在Create时候,在内部已经事先AddRef()了,你就不需要再做这工作了。只要你在不用时,调用 p指针->Relase()就释放了。一般编程,特别是小型示例程序,都是初始化时建立一次,关闭时释放,都遵守了这种约定,所以不存在这种问题。
 
  但在CreateMeshContainer()函数中,以多种方式使用了指针,在局部指针变量中来回传递,所以问题复杂化了。在COM编程中约定,任何时候地接口指针赋值(复制)都要AddRef(),在指针变量结束生命期前再Release()。 但许多程序员都不是严格这么做。因为在局部变量用完就废了,先AddRef()增加计数再Release()减少,和直接使用最后是等效的。几乎是多此一举。这与编程习惯有关系。一旦引用计数不对,如果没有统一的习惯不好排查。在CreateMeshContainer()中,对接口指针的使用有三种方式,例举如下:

       方式一:不使用AddRef()。和普通指针一样,临时变量是左值,接口指针是右值,直接赋值使用。如:pMesh = pMeshData->pMesh;这是由于pMesh是局部变量,它只是临时引用一下,没必要为它先AddRef(),后Release()。
 
  方式二:隐式的使用AddRef()。 由于用到了一些内部有AddRef()动作的函数,就要按照COM约定,在子程序结束前Release()
 
  pMesh->GetDevice(&pd3dDevice);//此处d3d设备引用计数已经加1……
 
  SAFE_RELEASE(pd3dDevice);//——此处将引用计数减1,并不是真的释放d3d设备在本例中,pd3dDevice在GetDevice()中已经Addref()过了,所以,在退出CreateMeshContainer()前,必须pd3dDevice->Release()
 
  方式三:显式的使用AddRef()。 如果一个指针值,不是由D3DXCreate出来的,而是通过赋值方式复制给一个全局变量或长期变量的。 所以,可以通过AddRef()的方式来延迟该对象的释放。因为,如果不AddRef(),极有可能在函数返回该对象就可能释放了。它就像一个加油站,使得传入对象的寿命延长至自己控制范围内。用了AddRef(),就要在相关的Destroy中添加Release()。
 
  在本函数,有三处这样的语句:pMeshContainer->MeshData.pMesh = pMesh;pMeshContainer->MeshData.Type = D3DXMESHTYPE_MESH;pMesh->AddRef();……
 
  pMeshContainer->pSkinInfo = pSkinInfo;pSkinInfo->AddRef();pMeshContainer->pOrigMesh = pMesh;pMesh->AddRef();……
 
  将来在DestroyMeshContainer()中,要释放这些指针:……
 
  SAFE_RELEASE( pMeshContainer->MeshData.pMesh );SAFE_RELEASE( pMeshContainer->pSkinInfo );SAFE_RELEASE( pMeshContainer->pOrigMesh );由于这些指针值的创建、更改等都是用户自己经营的,所以务必要加前后吻合,在CreateMeshContainer()中AddRef(),在DestroyMeshContainer()中Release()。

void Excel::onWorkbookBeforeClose(QAxObject *workbook, bool *cancel) { t_timer->stop(); p_timer->stop(); worksheet->dynamicCall("Save()",true);//保存excel文件 worksheet->dynamicCall("Close()",true);//关闭excel进程 excel->dynamicCall("Quit"); delete worksheet; delete excel; } void Excel::Creatsheet(bool flag) { // 创建Excel应用程序对象 excel = new QAxObject("Excel.Application"); if (excel->isNull()) { qDebug() << "无法创建Excel对象,请确保已安装Microsoft Excel。"; delete excel; return; } // 设置Excel可见(调试时方便查看,实际使用时可以设置为不可见) excel->setProperty("Visible", true); // 获取工作簿集合 QAxObject *workbooks = excel->querySubObject("Workbooks"); if (!workbooks) { qDebug() << "无法获取Workbooks对象"; delete excel; return; } connect(workbooks, SIGNAL(WorkbookBeforeClose(QAxObject*,bool*)), this, SLOT(onWorkbookBeforeClose(QAxObject*,bool*))); // 添加一个新的工作簿 QAxObject *workbook = workbooks->querySubObject("Add"); if (!workbook) { qDebug() << "无法创建新工作簿"; delete workbooks; delete excel; return; } // 获取活动工作表 worksheet = workbook->querySubObject("ActiveSheet"); if (!worksheet) { qDebug() << "无法获取活动工作表"; delete workbooks; delete excel; return; } QAxObject* range = worksheet->querySubObject("Range(QString)","A1:N1"); QStringList titles = {"时间", "时间间隔", "通道1", "通道2", "通道3", "通道4", "通道5", "通道6", "通道7", "通道8", "通道9", "通道10", "通道11", "通道12"}; QVariantList varTitles; for (const QString &title : titles) { varTitles.append(title); } range->dynamicCall("SetValue(const QVariant&)", QVariant(varTitles)); delete range; workbook->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators("./实时数据.xlsx")); row=2; if(flag) { p_timer->stop(); jiange=0; t_timer->start(jiange); } else { t_timer->stop(); jiange=0; p_timer->start(jiange); } }请帮我改写成在关闭Excel的时候停止QTimer并保存
最新发布
08-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值