处理比较:空类与未定义类相比无效*

时间:2010-12-24 10:47:05

标签: c++ handles incomplete-type empty-class

Microsoft的GDI+定义了许多在内部被视为句柄的空类。例如,(来源GdiPlusGpStubs.h

//Approach 1

class GpGraphics {};

class GpBrush {};
class GpTexture : public GpBrush {};
class GpSolidFill : public GpBrush {};
class GpLineGradient : public GpBrush {};
class GpPathGradient : public GpBrush {};
class GpHatch : public GpBrush {};

class GpPen {};
class GpCustomLineCap {};

还有其他两种定义句柄的方法。他们是,

//Approach 2
class BOOK;  //no need to define it!
typedef BOOK *PBOOK;
typedef PBOOK HBOOK; //handle to be used internally

//Approach 3
typedef void* PVOID;
typedef PVOID HBOOK; //handle to be used internally

我只想知道每种方法的优点和缺点。

Microsoft的方法的一个优点是,他们可以使用空类定义类型安全的 句柄层次结构,而我认为这是其他两个不可能的方法,虽然我想知道这个层次结构会给实现带来什么好处?无论如何,还有什么?

编辑:

第二种方法的一个优点(即使用不完整的类)是我们可以阻止客户端解除引用句柄(这意味着,这种方法似乎强烈支持封装,我想)。如果尝试取消引用句柄,代码甚至不会编译。还有什么?

与第三种方法相同的优点是,你不能取消引用句柄。

3 个答案:

答案 0 :(得分:2)

方法3并不是很好,因为它允许混合和匹配实际上没有意义的句柄类型,任何带有HANDLE的函数都可以使用任何HANDLE,即使它的编译时间可以确定是错误的类型。

方法1的缺点是你必须在另一端做一堆铸造它们的实际类型。

方法2并没有那么糟糕,除非你不能用它进行任何类型的继承,而不必每次都进行外部查询。

然而,自编译器发现如何实现高效的虚函数以来,所有这些都完全没有实际意义。 DirectX和COM采用的方法是最好的 - 它非常灵活,功能强大,完全类型安全。

它甚至允许一些真正疯狂的东西,比如你可以从DirectX接口继承并以这种方式扩展它。其中一个最好的优点是Direct2D和Direct3D11。它们实际上并不兼容(这是真正的,可怕的愚蠢),但你可以定义一个继承自ID3D10Device1的代理类型并转发到ID3D11Device并解决这样的问题。这种事情甚至都不会考虑采用上述任何一种方法。

哦,最后一件事:你真的,真的不应该用allcaps命名你的类型。

答案 1 :(得分:2)

方法#1是C风格和C ++界面之间的中间部分。您必须将句柄作为参数传递,而不是成员函数。暴露多态的优点是可以减少接口中的函数数量,并在编译时检查类型。通常大多数专家更喜欢pimpl习语(有时称为编译防火墙)到这样的界面。你不能使用方法#1与C接口,所以最好去完整的C ++。

方法#2是C风格封装和信息隐藏。指针可能(通常是)指向真实事物的指针,因此它不会过度设计。库的用户可能无法取消引用该指针。缺点是它不会暴露任何多态性。优点是您可以在与用C语言编写的模块连接时使用它。

方法#3是过度抽象的C风格封装。指针实际上可能根本不是指针,因为库的用户不应该转换,解除分配或取消引用它。优点是它可能带有异常或错误值,缺点是大部分都必须检查运行时间。

我同意DeadMG的看法,语言中立的面向对象的接口在C ++中使用起来非常简单和优雅,但是这些也涉及比编译时间检查更多的运行时检查,并且当我不需要与其他语言。所以我个人更喜欢方法#2,如果它只需要与C或Pimpl成语接口,那么它只是C ++。

答案 2 :(得分:1)

2和3的类型安全性稍差,因为它们允许使用句柄而不是void *

void bluescreeen(HBOOK hb){
  memset(hb,0,100000); // no compile errors
}