Comparación de manijas: clases vacías frente a clases indefinidas frente a nulas *

De MicrosoftGDI + define muchas clases vacías para ser tratadas como manejadores internamente. Por ejemplo, (fuenteGdiPlusGpStubs.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 {};

Hay otras dos formas de definir los identificadores. Ellos son,

//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

Solo quiero saber las ventajas y desventajas de cada uno de estos enfoques.

Una ventaja del enfoque de Microsoft es que pueden definirtipo seguro jerarquía de manijas usando clases vacías, lo que (creo) no es posible con los otros dos enfoques, aunque me pregunto qué ventajas aportaría esta jerarquía a la implementación. De todos modos, ¿qué más?

EDITAR:

Una ventaja con el segundo enfoque (es decir, el uso de clases incompletas) es que podemos evitar que los clientes desreferencian los identificadores (eso significa, este enfoque parece admitir encapsulación fuertemente, supongo). El código ni siquiera se compilaría si se intenta desreferenciar los identificadores. ¿Qué más?

La misma ventaja que se tiene con el tercer enfoque también es que no se puede desreferenciar los controladores.

Respuestas a la pregunta(3)

Su respuesta a la pregunta