使用CoTaskMemAlloc时,我应该总是调用CoTaskMemFree吗?

问题描述 投票:3回答:3

我正在编写一些COM和ATL代码,由于某种原因,所有代码都使用CoTaskMemAlloc来分配内存而不是newmalloc。所以我遵循这种编码风格,我也使用CoTaskMemAlloc

我的老师教我在分配记忆时总是使用deletefree。但是,如果我使用CoTaskMemFree,我不确定是否应该总是调用CoTaskMemAlloc

memory-management com atl
3个回答
8
投票

使用CRT提供的new / malloc和delete / free是COM互操作中的一个问题。为了使它们工作,CRT的同一副本分配和释放内存是非常重要的。在COM互操作方案中执行是不可能的,您的COM服务器和客户端实际上保证使用不同版本的CRT。每个使用自己的堆来分配。这会导致Windows XP上出现无法识别的内存泄漏,这在Vista及其上是一个很难的例外。

这就是COM堆存在的原因,即服务器和客户端同时使用的进程中的单个预定义堆。 IMalloc是访问该共享堆的通用接口,CoTaskMemAlloc()和CoTaskMemFree()是系统提供的使用该接口的辅助函数。

也就是说,只有在服务器分配内存且客户端必须释放内存的情况下才需要这样做。或者相反。在互操作场景中应该总是很少见,事故的几率太大了。在COM自动化中,只有两种情况,即BSTR和SAFEARRAY,已经包装的类型。在其他情况下,通过让方法调用者提供内存并且被调用者填充它来避免它。这也允许强大的优化,内存可以来自调用者的堆栈。

查看代码并检查谁分配内存以及谁需要释放内存。如果两者都存在于同一个模块中,那么使用new / malloc就可以了,因为现在很难保证同一个CRT实例可以处理它。如果不是这种情况,那么考虑修复它,以便调用者提供内存并释放它。


3
投票

内存的分配和释放必须始终来自同一来源。如果你使用CoTaskMemAlloc那么你必须使用CoTaskMemFree释放内存。

请注意,在C ++中,管理内存和对象构造/破坏(new / delete)的行为是独立的操作。可以自定义特定对象以使用不同的内存分配器,并且仍然允许使用首选的标准new / delete语法。例如

class MyClass { 
public:
  void* operator new(size_t size) {
    return ::CoTaskMemAlloc(size);
  }
  void* operator new[](size_t size) {
    return ::CoTaskMemAlloc(size);
  } 
  void operator delete(void* pMemory) {
    ::CoTaskMemFree(pMemory);
  }
  void operator delete[](void* pMemory) {
    ::CoTaskMemFree(pMemory);
  }   
};

现在,我可以像任何其他C ++类型一样使用此类型,但内存将来自COM堆

// Normal object construction but memory comes from CoTaskMemAlloc
MyClass *pClass = new MyClass();
...  
// Normal object destruction and memory freed from CoTaskMemFree
delete pClass;

1
投票

这个问题的答案是:是的,你应该使用CoTaskMemFree来释放用CoTaskMemAlloc分配的内存。

其他答案很好地解释了为什么CoTaskMemAlloc和CoTaskMemFree是COM服务器和COM客户端之间传递的内存所必需的,但他们没有直接回答您的问题。

你的老师是对的:你应该总是对任何资源使用相应的发布功能。如果您使用new,请使用delete。如果您使用malloc,请免费使用。如果使用CreateFile,请使用CloseHandle。等等。

更好的是,在C ++中,使用RAII对象在构造函数中分配资源并在析构函数中释放资源,然后使用那些RAII包装器而不是裸函数。这使得编写不泄漏的代码变得更容易和更清晰,即使你得到类似异常的东西。

标准模板库提供了实现RAII的容器,这就是为什么你应该学习使用std :: vector或std :: string而不是分配裸内存并尝试自己管理它的原因。还有一些智能指针,如std :: shared_ptr和std :: unique_ptr,可用于确保始终在正确的时间进行正确的释放调用。

ATL提供了一些类,如ATL :: CComPtr,它们是包装对象,可以为您处理COM对象的引用计数。它们使用起来并不是万无一失的,事实上,它比大多数现代STL类还要多一些,所以请仔细阅读文档。如果使用正确,可以相对容易地确保AddRef和Release调用都匹配。

© www.soinside.com 2019 - 2024. All rights reserved.