从函数返回堆分配的指针是不是不礼貌

Is it bad manners to return a heap allocated pointer from a function?

本文关键字:指针 是不是 不礼貌 分配 函数 返回      更新时间:2023-10-16

boost::shared_ptr之前,从函数返回堆分配的指针是否被认为是一种糟糕的做法,因为调用方需要记住free()该对象?

或者,它被认为是"正常的"?

我不认为这是一种糟糕的做法,只要您的API还提供了一个等效的XXX_free(或XXX_closeXXX_clearup或其他)函数,客户端代码可以在完成指针时调用该函数。

这样,您就有了一个一致的、对称的API,也就是说,堆对象的生命周期的责任保持在一个地方。

这种方法也适用于更复杂的资源释放。例如,如果返回的指针指向一个动态分配的结构,而该结构的成员又指向动态分配的内存,那么整个清理过程可以从客户端代码中隐藏/抽象出来。

您已经标记了问题C。在C中,这是非常常见的,例如fopen返回FILE *,稍后必须通过调用fclose来解除分配。

如果你想把它标记为C++,那么它就更复杂了。较旧的代码库(20世纪90年代中期及更早)经常传递指向对象的裸指针。整个商业支持的库都是基于这种模式(Borland的OWL,Microsoft MFC)。

如果需要这样做,通常的做法是提供自己的"free"函数,该函数会占用您分配的指针并释放它。这可以防止用户使用不兼容的free实现,从而损坏内存。

您可以找到这两种方法的示例:要么是分配内存并返回指针的函数,要么是接受指向已分配空间的指针的函数。

只要接口有明确的文档记录和遵循,两者都有积极的一面和消极的一面。例如,正如许多其他人所提到的,提供分配功能的库通常应该提供删除功能。因为你,客户端,不知道在这个奇特的函数中使用了什么方法来分配内存,你(客户端)也不知道应该使用什么方法来销毁它

另一方面,当您需要担心存储的分配,将其传递给可能执行或可能不执行预期工作的东西,然后确定该存储是否仍然相关等时,逻辑可能会更加复杂。根据对内存的使用,隐藏分配细节也有助于封装它的一些优化。

简短的回答是:这取决于你。真正错误的做法是选择一些不一致或界面不清晰的东西。

它过去很常见,现在仍然很常见,因为它几乎是处理在某些操作系统上针对不同运行时构建的库的唯一方法。例如,在Windows上,在一个可执行文件中共享多个VC++运行库通常只有在库负责自己的所有内存管理(包括对象的分配和处理)的情况下才能正常工作。shared_ptr和类似的工具实际上会在这种情况下引发问题。

话虽如此,通常情况下,函数名称和/或文档(如果操作正确)会使这种情况明显发生。使用相应的DeleteXXX()函数来处理空闲调用也是很常见的。

通常,您只会从显式create_blah()函数返回一个指针

更常见的是传入一个指针(指向指针),如果它为null,则由函数分配。

实际上,一个更好的习惯用法(COM接口使用)是要求在函数调用点之前在调用方创建指针,并编写一个接受双指针的函数。

例如:

HRESULT CreateDevice([in]UINT适配器,[in]D3DDEVTYPE设备类型,[in]HWND hFocusWindow,[in]DWORD行为标志,[in,out]D3DPRESENT_PARAMETERS*pPresentationParameters,[out,retval]IDirect3DDevice9**ppReturnedDeviceInterface);

因此,调用者负责创建指针和调用分配函数,因此更有可能记住调用deallocation函数(在COM的情况下,它需要在COM对象上调用->Release()方法)

但我认为采用创建/销毁函数,再加上传递双指针,是一种更好的方式,可以提醒接收者在使用对象后要清理的堆分配对象。

我同意Oli的观点,即创建/销毁函数更加对称,并且销毁函数的存在应该让API用户意识到,他从创建函数中获得的这些对象不会自行消失,并且需要调用它们(销毁fcn)。