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

时间:2012-04-07 15:11:34

标签: memory-management com atl

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

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

3 个答案:

答案 0 :(得分:8)

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

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

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

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

答案 1 :(得分: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;

答案 2 :(得分: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调用都匹配。