从函数返回堆分配的指针是不礼貌的行为吗?

发布于 2024-11-16 10:19:32 字数 110 浏览 1 评论 0原文

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

或者说,这被认为是“正常”的吗?

Before boost::shared_ptr, was it considered a bad practice to return a heap allocated pointer from a function, since the caller will be required to remember to free() that object?

Or, was it considered "normal"?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

九歌凝 2024-11-23 10:19:32

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

这样,您就拥有了一致、对称的 API,从某种意义上说,堆对象的生命周期责任由一个地方维护。

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

I don't consider it bad practice, so long as your API also provides an equivalent XXX_free (or XXX_close, XXX_clearup, or whatever) function, that the client code can call when finished with the pointer.

That way, you have a consistent, symmetrical API, in the sense that responsibility for the lifetime of a heap object is maintained in one place.

This approach is also amenable to more-complex resource freeing. For example, if the pointer that gets returned is to a dynamically-allocated struct that in turn has members that point to dynamically-allocated memory, the entire cleanup procedure can be hidden/abstracted from the client code.

帅哥哥的热头脑 2024-11-23 10:19:32

您已将您的问题标记为C。在C 中,这是非常常见的,例如fopen 返回FILE *,稍后必须通过调用fclose 来释放它。

如果您打算将其标记为 C++,那么它会更复杂。较旧的代码库(20 世纪 90 年代中期及更早)经常传递指向对象的裸指针。整个商业支持的库都基于该模式(Borland 的 OWL、Microsoft MFC)。

You've tagged your question C. In C, it's very commonplace, e.g. fopen returning FILE * which must later be deallocated by calling fclose.

If you meant to tag it C++, then it's more complicated. Older codebases (mid-1990s and earlier) frequently passed around naked pointers to objects. Whole commercially supported libraries were based on that pattern (Borland's OWL, Microsoft MFC).

海未深 2024-11-23 10:19:32

如果您需要这样做,通常的做法是提供您自己的“免费”函数,该函数获取分配的指针并释放它。这可以防止用户使用不兼容的自由实现来损坏内存。

If you need to do this, it's common practice to provide your own "free" function that takes your allocated pointer and free it. This prevent user to use an incompatible free implementation that would corrupt memory.

爱要勇敢去追 2024-11-23 10:19:32

您将找到两种方法的示例:分配内存并返回指针的函数,或者接受指向已分配空间的指针的函数。

只要清楚地记录并遵循该界面,两者都有积极和消极的一面。例如,正如许多其他人所提到的,提供分配函数的库通常应该提供删除函数。由于您(客户端)不知道在该奇特函数中使用什么方法来分配内存,因此您(客户端)也不知道应该使用什么方法来销毁它。

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

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

You'll find examples of both methodologies: either a function allocating memory and returning a pointer, or a function accepting a pointer to already allocated space.

As long as the interface is clearly documented and followed, there are positives and negatives for both. For example, as many other people have mentioned, a library providing an allocation function typically should provide a delete function. As you, the client, don't know what method was used to allocate memory in that fancy function, you (the client) don't know what method should be used to destroy it.

On the other hand, the logic can be more complex when you need to worry about allocation of storage, passing that off to something that may or may not perform the work expected, and then determining if that storage is still pertinent, etc. Depending on the use of the memory, hiding away the allocation details could help encapsulate some optimization of it as well.

Short answer is: it's up to you. The really wrong way to go is to pick something and either be inconsistent or unclear in the interface.

神也荒唐 2024-11-23 10:19:32

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

话虽这么说,通常,函数名称和/或文档(如果正确完成)会明显表明这种情况正在发生。有一个相应的DeleteXXX()函数来处理免费调用也很常见。

It was, and is still common, as it's pretty much the only way to handle libraries built against different runtimes on certain operating systems. For example, on Windows, sharing multiple VC++ runtime libraries in a single executable typically will only work correctly if the libraries are responsible for all of their own memory management, including both the allocation of the objects as well as the disposal. shared_ptr and similar tools actually can cause issues in that case.

That being said, typically, the function name and/or documentation (when done correctly) would make it obvious this was happening. It was also common to have a corresponding DeleteXXX() function to handle the free call.

蓦然回首 2024-11-23 10:19:32

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

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

Generally you would only return a pointer from an explicit create_blah() function

More common is to pass in a pointer (to a pointer), which if it is null was allocated by the function.

暮光沉寂 2024-11-23 10:19:32

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

例如

    HRESULT CreateDevice(
      [in]           UINT Adapter,
      [in]           D3DDEVTYPE DeviceType,
      [in]           HWND hFocusWindow,
      [in]           DWORD BehaviorFlags,
      [in, out]      D3DPRESENT_PARAMETERS *pPresentationParameters,
      [out, retval]  IDirect3DDevice9 **ppReturnedDeviceInterface
    );

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

但我认为采用创建/销毁函数并结合传递双指针是提醒堆分配对象的接收者在使用该对象后进行清理的更好方法。

我同意 Oli 认为 Create/Destroy 函数更加对称,并且 Destroy 函数的存在应该让 API 用户认识到这些对象是他自己创建的。从 Create 函数获取的内容不会自行消失,并且需要调用它们(Destroy fcns)。

Actually a better idiom (used by COM interfaces) is to require pointer creation at the caller before the point of the function call, and write a function that accepts a double pointer.

For example:

    HRESULT CreateDevice(
      [in]           UINT Adapter,
      [in]           D3DDEVTYPE DeviceType,
      [in]           HWND hFocusWindow,
      [in]           DWORD BehaviorFlags,
      [in, out]      D3DPRESENT_PARAMETERS *pPresentationParameters,
      [out, retval]  IDirect3DDevice9 **ppReturnedDeviceInterface
    );

So, the caller is responsible for creating the pointer and calling the allocation function, and so is more likely to remember to call the deallocation function (which in COM's case requires you call a ->Release() method on the COM object)

But I think adopting Creation/Destruction functions, combined with passing double pointers, is a better way to remind the recipient of a heap allocated object to clean up after using the object.

I agree with Oli that Create/Destroy functions are much more symmetrical and the existence of the Destroy function should turn the API user onto the fact that these objects he gets from the Create functions won't just disappear on their own, and they (Destroy fcns) need to be called.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文