Release() 和析构函数之间的区别?

Difference between Release() and destructor?

本文关键字:区别 之间 析构函数 Release      更新时间:2023-10-16

当我学习使用抽象接口将代码封装在动态库中时,似乎建议使用"Release(("函数来释放资源。现在我想知道为什么他们不只使用析构函数来发布?使用析构函数释放资源是否有任何问题,或者他们只是想使用智能指针?

这是推荐的代码:

// Interface like that ..
struct IXyz
{
virtual int Foo(int n) = 0;
virtual void Release() = 0;
};
// Xyz class definition, derived from IXyz
// ...

// Using in client ..
IXyz* pXyz = GetXyz();  //Use Factory function to create an object
if(pXyz)
{
pXyz->Foo(42);
pXyz->Release();
pXyz = nullptr;
}

现在我想改为编写以下代码:

// Interface
struct IXyz
{
virtual int Foo(int n) = 0;
virtual ~IXyz() {};    // I have moved Release()'s content into Xyz's destructor
};

// Client
IXyz* pXyz = GetXyz();  //Use Factory function to create an object
if(pXyz)
{
pXyz->Foo(42);
pXyz->~IXyz();
pXyz = nullptr;
}

这些代码都可以正常工作。所以我想知道这两种释放资源的方式之间的区别。我可以同时使用它们吗?多谢!

这只在Windows上是一个问题。通过 new 分配内存时,Win32可以使用DLL 或 EXE 的本地分配堆。这可能会导致在发布生成 DLL 中分配,但稍后在调试生成 EXE 中释放的问题。然后,调试堆将抛出一个错误,指出内存分配未知。

因此,要解决此问题,一般方法是确保为 DLL 堆中的 DLL 对象分配内存,并在同一堆中释放内存。

首先,让我们尝试上面描述的工厂方法。提供 DLL 导出的工厂方法来分配内存,例如

struct IXyz
{
EXPORT static IXyz* create();
};
// in cpp file
EXPORT IXyz* IXyz::create()
{
return new IXyz;
}

但是,现在这意味着您必须在同一位置释放内存,您可以通过以下两种方式之一执行此操作。选项一,提供 DLL 导出的发布方法。

struct IXyz
{
EXPORT static IXyz* create();
EXPORT void release();
};
// in cpp file
EXPORT void IXyz::release()
{
delete this;
}

另一种方法是使 dtor 虚拟化。它将确保在同一位置释放分配,但会通过 vtable 指针使结构大小膨胀。

如果您不想这样做,另一种方法是从基本 DLL(简单地调用全局 new/delete(导出一对自定义 malloc/free 函数。然后在从 DSO 导出的每个类中重载 new/delete。使用new/delete将不再给您带来问题。

除非你实际上大量使用DLL,并且你需要混合调试/发布版本,否则以上所有内容都是完全没有意义的。这些模式有其位置,但它们通常不是很好的模式。

首先,调用析构函数会破坏对象,但不会释放它的内存。事实上,很少需要完全调用析构函数,因为delete(对于动态分配的对象(或超出范围(对于在堆栈/全局范围内分配的对象(已经调用析构函数并照顾内存。

现在,Release()实际上做了一些非常不同的事情:它是(至少在我所知道的所有库中(称为引用计数的一部分。这是一种控制何时应delete对象 -ed 的方法,具体取决于使用它的内容数量。因此,虽然调用析构函数(显式或通过调用delete(肯定会销毁对象,但调用Release()只是告诉库您不再使用该对象,并且库可以随时自由删除它 - 现在可能不是,因为库可能仍在内部使用该对象。