Да, вы правы, что такие примеры плохо написаны. Они должны быть написаны так, как вы описали:
ULONG __stdcall Release()
{
if (InterlockedDecrement((long*)&RefCount) == 0) {
delete this;
return 0;
}
return RefCount;
}
Однако для них лучше просто вернуть любой результат, возвращаемый InterlockedDecrement
. Как отметил @RaymondChen в комментариях, это также решает проблему уменьшения RefCount
другим потоком, потенциально уничтожая this
, до достижения return
, например:
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()
, который вы показали, также написан неправильно, поскольку он не увеличивает RefCount
при возврате S_OK
, например:
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;
}
Я также видел, как это написано так, что объясняет плохие случаи, когда QueryInterface()
вызывается на указателе NULL
:
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;
}