【发布时间】:2011-06-20 13:38:48
【问题描述】:
最近我发现了一个很好的例子来说明为什么 C 风格的强制转换不好。我们从实现多个 COM 接口的以下类开始(为了简洁起见,我有两个,但在现实生活中可能有十个):
class CMyClassInitial : public IInterface1, public IInterface2 {
//declarations omitted
};
HRESULT CMyClassInitial::QueryInterface(REFIID iid, void** ppv)
{
if( ppv == 0 ) {
return E_POINTER;
}
*ppv = 0;
if( iid == __uuidof(IUnknown) || iid == __uuidof(IInterface1) ) {
*ppv = (IInterface1*)this;
} else if( iid == __uuidof(IInterface2) ) {
*ppv = (IInterface2*)this;
} else {
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
上面的实现对adjusting pointers to account for multiple inheritance 使用了C-casts。它们甚至可以作为 static_casts 工作 - this 指针值将被适当调整。
现在我们将相同的QueryInterface() 实现复制粘贴(或者我应该说重用代码?)到其他一些非常相似的类。
class CMyClassModified : public IInterface1 {
//declarations omitted
};
并保持实现不变。新类不再继承自 IInterface2,而是
} else if( iid == __uuidof(IInterface2) ) {
*ppv = (IInterface2*)this;
}
将编译得很好,C 风格的转换将充当reinterpret_cast - this 指针值将被复制而不变。 调用者将获得一个指向实际上并未实现 IInterface2 的对象的指针 - 直接导致未定义行为。在庞大的数据库中以及当有很多(不是我的示例中的两个)接口时,很难发现此类问题。
如果使用了static_cast,这将不会发生 - 编译器会在尝试编译时发出错误
*ppv = static_cast<IInterface2*>(this);
IMO 这是一个足够严酷的例子,说明使用 C 风格的强制转换会导致严重的问题。
还有哪些例子?
【问题讨论】:
-
一个很好的陷阱,但我不完全确定这是否适合 SO。似乎很有讨论性。充其量它是一个社区维基。
-
@tenpn:我看不出这里可以讨论什么——只是一个用 C++ 射击自己腿的例子。
-
@sharptooth 但这不是问题,不是吗?
-
@tenpn:嗯...我问还有什么其他的例子。这不是问题吗?
-
这也可以作为一个例子,说明为什么复制粘贴代码(尤其是没有彻底阅读它)通常是一个坏主意。