我正在编写一些COM和ATL代码,由于某些原因,所有代码都使用CoTaskMemAlloc
来分配内存,而不是new
或malloc
。所以我遵循这种编码风格,我也使用CoTaskMemAlloc
。
我的老师教我在分配记忆时总是delete
或free
。但是,如果我使用CoTaskMemFree
,我不确定是否应该始终致电CoTaskMemAlloc
?
答案 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调用都匹配。