GDI/DirectDraw内部数据结构(一)
句柄和面向对象的编程
(1)Win32 API中的对象可以认为是使用没有数据成员的抽象基类实现的。对象的数据表示对用户应用程序是完全隐蔽的,这样可以大
大改善了程序的可移植性。下面是C++中的GDI伪执行:
//gdi.h
class _GdiObj
{
public:
virtual int GetObjectType() = 0;
virtual int GetObject(int cbBuffer,void * pBuffer) = 0;
virtual bool DeleteObject() = 0;
virtual bool UnrealizeObject() = 0;
};
class _Pen : public _GdiObj
{
public:
virtual int GetObjectType()
{
return OBJ_PEN;
}
virtual int GetObject(int cbBuffer,void * pBuffer) = 0;
virtual bool DeleteObject() = 0;
virtual bool UnrealizeObject()
{
return true;
}
};
_Pen * _CreatePen(int fnPenStyle,int nWidth,COLORREF crColor);
//gdi.cpp
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "gdi.h"
class _RealPen : public _Pen
{
LOGPEN m_LogPen;
public:
_RealPen(int fnPenStyle,int nWidth,COLORREF crColor)
{
m_LogPen.lopnStyle = fnPenStyle;
m_LogPen.lopnWidth.x = nWidth;
m_LogPen.lopnWidth.y = 0;
m_LogPen.lopnColor = crColor;
}
int GetObject(int cbBuffer,void * pBuffer)
{
if(pBuffer == NULL)
return sizeof(LOGPEN);
else if(cbBuffer >= sizeof(m_LogPen))
{
memcpy(pBuffer,&m_LogPen,sizeof(m_LogPen));
return sizeof(LOGPEN);
}
else
{
SetLastError(ERROR_INVALID_PARAMETER);
return 0;
}
}
bool DeleteObject()
{
if(this)
{
delete this;
return true;
}
else
return false;
}
};
_Pen *_CreatePen(int fnPenStyle,int nWidth,COLORREF crColor)
{
return new _RealPen(fnPenStyle,nWidth,crColor);
}
//test.cpp
void Test()
{
_Pen *pPen = _CreatePen(PS_SOLID,1,RGB(0,0,0xEF));
pPen->DeleteObject();
}
上面的程序用_GdiObj类为常用GDI对象定义了抽象基类。它只有四个纯虚函数,没有数据成员。通用画笔类_Pen从_GdiObj派生。它
实现了两个虚函数并让剩下的两个函数仍是纯虚函数。它提供了CreatePen例程来创建Pen类的实例。在实现文件( GDI.cpp)中,真
正的画笔类(_RealPen)使用LOGPEN结构存储关于画笔的信息。它完全实现了抽象类_Pen,实现了一个构造函数和剩下的两个纯虚函
数。_CreatePen例程式创建_RealPen类的实例,并把它的指针作为通用画笔类_Pen的指针传入.
(2)指针与句柄:在面向对象评议中创建对象,需要分配一块保存对象成员变量的内在。如果它的类有虚函数,就要再分配一个额外的
指针,并将它赋值成指向该类所有虚函数实现全程表的指针。在C++这样的语言中,指向对象的指针很重要。它被传递给该类的所有
非静态成员函数,这样才能访问该对象的数据成员,调用正确的虚函数。这样的指针被称为C++中的"this"指针。
上面程序所示的_RealPen类说明了对象指针所指的内容。对于_RealPen类,需要用16个字节存储唯一的数据成员m_LogPen,4个字节存
储vtable(虚函数表)指针。因此每个对象至少要分配20个字节。 vtable指针指向一个有4个函数指针的表。在这种情况下,其中两个
函数是在_Pen类实现,加外两个函数由_RealPen类实现。
(3)为了对程序员进一步隐藏,Win32对象创建例程一般不是返回指针,因为指针包含了太多信息。它给出了对象存储的确切位置。指
针一般允许对对象的内部表示进行读/写操作,而这些内部表示也许正是操作系统想隐藏的。所以Win32对象创建例程一般返回对象句
柄。句柄可以映射为唯一标识对象的值,或者是对象的间接引用。更准确地说,句柄是和对象一一对应的值。对象可以映射到唯一的
句柄,句柄也能够映射到唯一的对象。为了保证句柄能够完成信息隐藏的任务,对象和句柄之间映射的没有文档记载,不保证固定不
变,而且仅有微软知道这种映射,或者还有少数系统级工具的开发商知道。
(4)在极端的情况下,句柄可以和对象指针相同,其之间的映射为全等映射。
(5)对象指针和句柄之间映射最普通的机制是基于表格的映射。
(6)只有句柄是不够的,尽管句柄提供了近乎完美的抽象、信息隐藏和保护,但它也是程序员遭受挫折的地方。在像Win32 API这样以
句柄为中心的API中,微软没有文档记载对象的内部表示以及对象是如何管理的,也没有提供参考实现程序员只有函数原型、微软文
档和或多或少基于微软文档之上的书籍。