我正在编写一些 COM 和 ATL 代码,出于某种原因,所有代码都用于CoTaskMemAlloc
分配内存而不是new
or malloc
。所以我遵循了这种编码风格,我也使用CoTaskMemAlloc
.
我的老师教我总是delete
或free
在分配内存时。但是我不确定如果CoTaskMemFree
我使用,我是否应该总是打电话CoTaskMemAlloc
?
我正在编写一些 COM 和 ATL 代码,出于某种原因,所有代码都用于CoTaskMemAlloc
分配内存而不是new
or malloc
。所以我遵循了这种编码风格,我也使用CoTaskMemAlloc
.
我的老师教我总是delete
或free
在分配内存时。但是我不确定如果CoTaskMemFree
我使用,我是否应该总是打电话CoTaskMemAlloc
?
使用 CRT 提供的 new/malloc 和 delete/free 是 COM 互操作中的一个问题。为了使它们工作,CRT 的同一个副本同时分配和释放内存是非常重要的。这在 COM 互操作场景中是不可能实现的,您的 COM 服务器和客户端实际上可以保证使用不同版本的 CRT。每个都使用自己的堆进行分配。这会在 Windows XP 上导致无法诊断的内存泄漏,在 Vista 及更高版本上是一个硬异常。
这就是 COM 堆存在的原因,它是服务器和客户端都使用的进程中的单个预定义堆。IMalloc 是访问共享堆的通用接口,CoTaskMemAlloc() 和 CoTaskMemFree() 是系统提供的帮助函数来使用该接口。
也就是说,这仅在服务器分配内存并且客户端必须释放它的情况下才有必要。或者反过来。在互操作场景中这应该总是很少见的,发生事故的可能性太大了。在 COM 自动化中只有两种这样的情况,一个 BSTR 和一个 SAFEARRAY,这两种类型已经被包装了。在其他情况下,您可以通过让方法调用者提供内存并让被调用者填充它来避免它。这也允许强大的优化,内存可以来自调用者的堆栈。
查看代码并检查谁分配了内存以及谁需要释放它。如果两者都存在于同一个模块中,那么使用 new/malloc 就可以了,因为现在硬保证同一个 CRT 实例会处理它。如果不是这种情况,请考虑修复它,以便调用者提供内存并释放它。
内存的分配和释放必须始终来自同一个源。如果你使用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;
问题的答案是:是的,您应该使用 CoTaskMemFree 来释放使用 CoTaskMemAlloc 分配的内存。
其他答案很好地解释了为什么 CoTaskMemAlloc 和 CoTaskMemFree 对于 COM 服务器和 COM 客户端之间传递的内存是必需的,但它们没有直接回答您的问题。
您的老师是对的:您应该始终对任何资源使用相应的发布功能。如果您使用新的,请使用删除。如果您使用 malloc,请免费使用。如果您使用 CreateFile,请使用 CloseHandle。等等。
更好的是,在 C++ 中,使用 RAII 对象在构造函数中分配资源并在析构函数中释放资源,然后使用这些 RAII 包装器而不是裸函数。这使得编写不会泄漏的代码变得更容易和更清晰,即使你得到了类似异常的东西。
标准模板库提供了实现 RAII 的容器,这就是为什么您应该学习使用 std::vector 或 std::string 而不是分配裸内存并尝试自己管理的原因。还有像 std::shared_ptr 和 std::unique_ptr 这样的智能指针,可用于确保始终在正确的时间进行正确的发布调用。
ATL 提供了一些类,例如 ATL::CComPtr,它们是为您处理 COM 对象的引用计数的包装对象。正确使用它们并不是万无一失的,事实上,它们比大多数现代 STL 类有更多的陷阱,因此请仔细阅读文档。如果使用得当,确保 AddRef 和 Release 调用都匹配相对容易。