为什么示例代码访问IUnknown中已删除的内存
Why does example code access deleted memory in IUnknown?
当使用IUnknown
等接口时,有很多例子,在这个例子中是IDocHostUIHandler
,但这并不重要-使用类似的代码:
class TDocHostUIHandlerImpl : public IDocHostUIHandler
{
private:
ULONG RefCount;
public:
TDocHostUIHandlerImpl():RefCount(0){ }
// IUnknown Method
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv) {
if (IsEqualIID(riid,IID_IUnknown))
{
*ppv = static_cast<IUnknown*>(this);
return S_OK;
}
else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
*ppv = static_cast<IDocHostUIHandler*>(this);
return S_OK;
}
else {
*ppv = NULL;
return E_NOINTERFACE;
}
}
ULONG __stdcall AddRef() {
InterlockedIncrement((long*)&RefCount);
return RefCount;
}
ULONG __stdcall Release() {
if (InterlockedDecrement((long*)&RefCount) == 0) delete this;
return RefCount;
}
我在这里的问题是Release()
方法,它删除了使用delete this
的接口实现,但紧接着删除了return RefCount
,它不再引用内存中的有效对象(它访问已删除的内存(。
我认为它应该是类似的东西
ULONG __stdcall Release() {
if (InterlockedDecrement((long*)&RefCount) == 0) { delete this; return 0; }
return RefCount;
}
这也不会触发我使用的资源泄漏工具(C++Builder中的Codeguard(。那么,为什么这么多例子都使用第一个版本,我在这里缺少什么呢?
或者只是";删除此";是否在Release
方法结束后在另一个编译器(如Visual Studio(中调用?
几个例子:
https://www.codeproject.com/Articles/4758/How-to-customize-the-context-menus-of-a-WebBrowser
在IUnknown中寻址和释放,它们实际上是做什么的?
https://bbs.csdn.net/topics/20135139
是的,这样的例子写得不好是正确的。它们需要写得更像你所描述的:
ULONG __stdcall Release()
{
if (InterlockedDecrement((long*)&RefCount) == 0) {
delete this;
return 0;
}
return RefCount;
}
然而,对于他们来说,最好只返回InterlockedDecrement
返回的任何结果。正如@RaymondChen在评论中指出的那样,这也解决了RefCount
被另一个线程递减的问题,可能会在达到return
之前破坏this
,例如:
ULONG __stdcall Release()
{
ULONG res = (ULONG) InterlockedDecrement((long*)&RefCount);
if (res == 0) {
delete this;
}
return res;
}
与AddRef()
相同,因此:
ULONG __stdcall AddRef()
{
return (ULONG) InterlockedIncrement((long*)&RefCount);
}
附带说明一下,您显示的QueryInterface()
示例也写错了,因为它在返回S_OK
时没有递增RefCount
,例如:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
if (IsEqualIID(riid,IID_IUnknown)) {
*ppv = static_cast<IUnknown*>(this);
AddRef(); // <-- add this!
return S_OK;
}
else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
*ppv = static_cast<IDocHostUIHandler*>(this);
AddRef(); // <-- add this!
return S_OK;
}
else {
*ppv = NULL;
return E_NOINTERFACE;
}
}
通常可以更容易地这样写:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
if (!ppv) {
return E_POINTER;
}
if (IsEqualIID(riid, IID_IUnknown)) {
*ppv = static_cast<IUnknown*>(this);
}
else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
*ppv = static_cast<IDocHostUIHandler*>(this);
}
else {
*ppv = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
我也看到它是这样写的,这说明了在NULL
指针上调用QueryInterface()
时的坏情况:
HRESULT __stdcall QueryInterface(REFIID riid, void **ppv)
{
if (!ppv) {
return E_POINTER;
}
if (IsEqualIID(riid, IID_IUnknown)) {
*ppv = static_cast<IUnknown*>(this);
}
else if (IsEqualIID(riid, IID_IDocHostUIHandler)) {
*ppv = static_cast<IDocHostUIHandler*>(this);
}
else {
*ppv = NULL;
}
if (*ppv) {
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
- 在c++中删除内存失败
- delete[]有问题,如何部分删除内存
- 在这种情况下,我必须删除内存吗?
- STD分配器是否会在堆上动态分配内存?它可以安全地删除内存吗?
- 我可以在使用 winsoc 发送缓冲区后删除内存吗?
- 最好的方法是删除内存泄漏
- com,删除内存,而不是调用版本()
- 如何调用返回后删除[]内存
- 删除内存空间后,指针的值保持不变还是更改?对象内容是否将被更改
- unique_ptr的矢量未被完全删除(内存泄漏)
- 在析构函数中安全地删除内存
- (C++) list.error 方法和使用指针删除内存
- 为结构C++中的指针分配和删除内存
- 正在读取指向已删除内存未定义行为的指针
- 何时会在单一实例中删除内存
- 删除内存时遇到困难
- 在 go 中删除内存
- 在C++中使用智能指针分配和删除内存的有效方法
- Cpp删除内存
- 手动调用析构函数后如何删除内存