C++ 这里是否存在资源泄漏/双重免费的可能性?
下面的示例(未编译,因此我不担保语法)从资源池中提取两个资源(未使用C++ 这里是否存在资源泄漏/双重免费的可能性?,c++,memory-leaks,C++,Memory Leaks,下面的示例(未编译,因此我不担保语法)从资源池中提取两个资源(未使用new分配),然后在特定事务期间将它们与MyClass“绑定”在一起 该事务在这里由myFunc实现,它试图通过跟踪这些资源的“所有权”来防止这些资源泄漏。当MyClass的实例化明显成功时,本地资源指针被清除。本地catch,以及析构函数~MyClass将资源返回到它们的池中(通过上述清除本地指针来保护双空闲) MyClass的实例化可能会失败,并在两个步骤中导致异常(1)实际内存分配,或(2)构造函数主体本身。我对#1没有问
new
分配),然后在特定事务期间将它们与MyClass“绑定”在一起
该事务在这里由myFunc
实现,它试图通过跟踪这些资源的“所有权”来防止这些资源泄漏。当MyClass
的实例化明显成功时,本地资源指针被清除。本地catch
,以及析构函数~MyClass
将资源返回到它们的池中(通过上述清除本地指针来保护双空闲)
MyClass的实例化可能会失败,并在两个步骤中导致异常(1)实际内存分配,或(2)构造函数主体本身。我对#1没有问题,但在#2的情况下,如果在设置了m#resA
&m#resB
之后引发异常。使~MyClass
和myFunc
的清理代码承担将这些资源返回其池的责任
这是合理的担忧吗
我已经考虑过,但不喜欢:
- 智能指针(如boost的共享指针)。我没有看到如何应用到资源池(除了在另一个实例中包装)
- 允许在此级别上发生双自由,但在资源池上进行保护
- 尝试使用异常类型-尝试推断如果捕获了
,MyClass没有获得所有权。这将需要构造函数中的try-catch来确保bad\u alloc
中的任何分配失败不会与分配MyClass的失败混淆ABC()…此处有更多代码…
class SomeExtResourceA;
class SomeExtResourceB;
class MyClass {
private:
// These resources come out of a resource pool not allocated with "new" for each use by MyClass
SomeResourceA* m_resA;
SomeResourceB* m_resB;
public:
MyClass(SomeResourceA* resA, SomeResourceB* resB):
m_resA(resA), m_resB(resB)
{
ABC(); // ... more code here, could throw exceptions
}
~MyClass(){
if(m_resA){
m_resA->Release();
}
if(m_resB){
m_resB->Release();
}
}
};
void myFunc(void)
{
SomeResourceA* resA = NULL;
SomeResourceB* resB = NULL;
MyClass* pMyInst = NULL;
try {
resA = g_pPoolA->Allocate();
resB = g_pPoolB->Allocate();
pMyInst = new MyClass(resA,resB);
resA=NULL; // ''ownership succesfully transfered to pMyInst
resB=NULL; // ''ownership succesfully transfered to pMyInst
// Do some work with pMyInst;
...;
delete pMyInst;
} catch (...) {
// cleanup
// need to check if resA, or resB were allocated prior
// to construction of pMyInst.
if(resA) resA->Release();
if(resB) resB->Release();
delete pMyInst;
throw; // rethrow caught exception
}
}
我看不出这个小代码有任何漏洞 如果构造函数抛出异常,则不会调用析构函数,因为该对象从不存在。因此,我也没有看到双重删除 赫伯·萨特的这篇文章:
- 构造函数在概念上转换为 适当大小的原始内存块 变成一个服从其规则的物体 不变量。对象的生命周期 直到它的构造函数才开始 成功完成。如果 构造函数以抛出 例外,这意味着它永远不会 已完成对象的创建,并且 设置其不变量-并在 异常构造函数的点 退出时,对象不仅不会 存在,但从未存在。
- 概念上的析构函数/处理器 将对象恢复为原始内存。 因此,就像所有其他 非私有方法, 析构函数/处理器假定为 “this”对象为 实际上是一个有效的对象 不变量成立。因此 析构函数/处理器仅在上运行 已成功构建对象。
我想这应该可以消除你的疑虑 只要把
if(pMyInst){…}
放在catch中的release/delete代码上就可以了。明确拥有所有权的经典用法是std::auto\u ptr
大概是这样的:
std::auto_ptr<SomeResourceA>(g_pPoolA->Allocate()) resA;
std::auto_ptr<SomeResourceB>(g_pPoolB->Allocate()) resB;
pMyInst = new MyClass(resA.release(),resB.release());
std::auto_ptr(g_pPoolA->Allocate())resA;
std::auto_ptr(g_pPoolB->Allocate())resB;
pMyInst=newmyclass(resA.release(),resB.release());
调用构造函数时,您可以转移所有权。您的代码很好。但为了让它更好,使用某种智能指针 编辑:例如,您可以使用共享\u ptr:
class SomeExtResourceA;
class SomeExtResourceB;
class MyClass {
private:
// These resources come out of a resource pool not allocated with "new" for each use by MyClass
shared_ptr<SomeResourceA> m_resA;
shared_ptr<SomeResourceB> m_resB;
public:
MyClass(const shared_ptr<SomeResourceA> &resA, const shared_ptr<SomeResourceB> &resB):
m_resA(resA), m_resB(resB)
{
ABC(); // ... more code here, could throw exceptions
}
}
};
void myFunc(void)
{
shared_ptr<SomeResourceA> resA(g_pPoolA->Allocate(), bind(&SomeResourceA::Release, _1));
shared_ptr<SomeResourceB> resB(g_pPoolB->Allocate(), bind(&SomeResourceB::Release, _1));
MyClass pMyInst(resA,resB);
// you can reset them here if you want, but it's not necessery:
resA.reset(), resB.reset();
// use pMyInst
}
class一些外部资源;
类外部资源B;
类MyClass{
私人:
//这些资源来自一个资源池,MyClass没有为每次使用分配“new”
共享的ptr m_resA;
共享资源;
公众:
MyClass(常数共享\u ptr&resA、常数共享\u ptr&resB):
m_resA(resA),m_resB(resB)
{
ABC();/…此处的更多代码可能引发异常
}
}
};
void myFunc(void)
{
共享资源(g_pPoolA->Allocate(),绑定(&SomeResourceA::Release,_1));
shared_ptr resB(g_pPoolB->Allocate(),bind(&SomeResourceB::Release,_1));
MyClass pMyInst(resA、resB);
//如果需要,您可以在此处重置它们,但这不是必需的:
resA.reset(),resB.reset();
//使用pMyInst
}
我发现使用RAII的解决方案要简单得多。这是您发布双重呼叫的机会:
void func()
{
MyClass a(resourceA, resourceB);
MyClass b(a);
}
哎呀
如果您对资源使用RIAA包装,那么犯错误的可能性将大大降低。这样做容易出错。您当前缺少MyClass上的复制构造函数和赋值运算符,这可能导致对Release()的双重调用,如上所示
由于处理资源的复杂性,一个类应该只拥有一个资源。如果您有多个资源,请将其所有权委托给它专用于其所有权的类,并在您的类中使用多个此类对象
编辑1
Lut us做出一些假设:
资源是共享和计数的。使用Acquire()递增计数,使用Release()递减计数。当计数为零时,它们将自动销毁
class ReferenceRapper
{
ReferenceBase* ref;
public:
ReferenceWrapper(ReferenceBase* r) : ref (r) {/* Pool set the initial count to 1 */ }
~ReferenceWrapper() { if (ref) { ref->Release();} }
/*
* Copy constructor provides strong exception guarantee (aka transactional guarantee)
* Either the copy works or both objects remain unchanged.
*
* As the assignment operator is implemented using copy/swap it also provides
* the strong exception guarantee.
*/
ReferenceWrapper(ReferenceWrapper& copy)
{
if (copy.ref) {copy.ref->Acquire();}
try
{
if (ref) {ref->Release();}
}
catch(...)
{
if (copy.ref)
{ copy.ref->Release(); // old->Release() threw an exception.
// Must reset copy back to its original state.
}
throw;
}
ref = copy.ref;
}
/*
* Note using the copy and swap idium.
* Note: To enable NRVO optimization we pass by value to make a copy of the RHS.
* rather than doing a manual copy inside the method.
*/
ReferenceWrapper& operator(ReferenceWrapper rhsCopy)
{
this->swap(rhsCopy);
}
void swap(ReferenceWrapper& rhs) throws ()
{
std::swap(ref, rhs.ref);
}
// Add appropriate access methods like operator->()
};
现在已经完成了艰苦的工作(管理资源)。编写真正的代码变得微不足道
class MyClass
{
ReferenceWrapper<SomeResourceA> m_resA;
ReferenceWrapper<SomeResourceB> m_resB;
public:
MyClass(ReferenceWrapper<SomeResourceA>& a, ReferenceWrapper<SomeResourceB>& b)
: m_resA(a)
, m_resB(b)
{
ABC();
}
};
void myFunc(void)
{
ReferenceWrapper<SomeResourceA> resA(g_pPoolA->Allocate());
ReferenceWrapper<SomeResourceB> resB(g_pPoolB->Allocate());
std::auto_ptr<MyClass> pMyInst = new MyClass(resA, resB);
// Do some work with pMyInst;
}
class-MyClass
{
参考包装纸m_resA;
参考包装纸m_resB;
公众:
MyClass(ReferenceWrapper&a、ReferenceWrapper&b)
:m_resA(a)
,m_resB(b)
{
ABC();
}
};
void myFunc(void)
{
ReferenceWrapper resA(g_pPoolA->Allocate());
引用包装器resB(g_pPoolB->Allocate());
std::auto_ptr pMyInst=新的MyClass(resA、resB);
//与pMyInst一起做一些工作;
}
根据以下注释编辑2,即资源只有一个所有者:
如果我们假设一个资源只有一个所有者,并且没有共享,那么它就变得微不足道:
class MyClass
{
std::auto_ptr<SomeResourceA> m_resA;
std::auto_ptr<SomeResourceB> m_resB;
public:
MyClass(std::auto_ptr<SomeResourceA>& a, std::auto_ptr<SomeResourceB>& b)
: m_resA(a)
, m_resB(b)
{
ABC();
}
};
void myFunc(void)
{
std::auto_ptr<SomeResourceA> resA(g_pPoolA->Allocate());
std::auto_ptr<SomeResourceB> resB(g_pPoolB->Allocate());
std::auto_ptr<MyClass> pMyInst = new MyClass(resA, resB);
// Do some work with pMyInst;
}