23.5 MFC classes are shallow wrappers
You might wonder what the members of the various kinds of MFC classes are. As it turns out, most of the classes have only one member, which is public. These members all have a name which starts with a lower-case m and an underscore: m_. Thus, for instance, the only data member of a CWnd
is a public HWND
called m_hWnd. What's an HWND, anyway? It means 'handle to a window.' In similar fashion, if you look inside a CDC, you'll find a public HDC
field called m_hDC, where an HDC
is a 'handle to a device context.'
Handles can be described as pointers to pointers. We can think of a handle as being like an index into a table of memory pointers. Saving an object's memory pointer at a second remove allows the operating system to move around the object's memory and change its memory pointer, while keeping the same handle pointer to locate the current value of the memory pointer.
As a practical matter, C++ really doesn't want you to worry about the data members of a class. You're supposed to use accessors to get information about what's in an object, use mutators to change an object's contents, and use the class methods to do things with the object. So usually we don't think too much about the m_
fields inside our classes. But now and then we'll notice that the implementation of an MFC method refers to one of the class's m_
data fields.
If you've ever worked with Win32 programming, you'll be familiar with the fact that Windows programming involves a number of special data whose names start with the letter H. An HWND
is a type that represents a window, an HDC
is a type that represents a 'device context,' an HPEN
is a kind of virtual drawing object called a 'pen,' and so on. These types all start with the letter H, because rather than being visible data structures, they are handles for hidden Windows data structures.
The basic idea for MFC is instead of using handles to secret data structures, we should start using real pointers to real C++ objects. This means two things. For just about every H
WHATEVER
type from Win32, MFC defines a corresponding C
Whatever
class. Thus, HWND
is replaced by CWnd, HDC
by CDC, HBRUSH
by CBrush, HICON
by CSpriteIcon, HBITMAP
by CBitmap, and so on.
In addition, most Win32 functions that had an H??? as the first argument type now become member functions of the corresponding C??? class. Thus GetDC(HWND hwnd)
becomes CWnd::GetDC(). And all of the graphics design interface (GDI) functions that had an HDC
as the first argument are now CDC
member functions; for instance Rectangle(HDC hdc, int left, int top, int right, int bottom)
is now CDC::Rectangle(int left, int top, int right, int bottom). The formerly explicit H
WHATEVER
argument is now an implicit C
Whatever
argument that appears as the object calling the method. Where formerly we wrote Rectangle(hdc, 10, 20, 100, 200), we'll now write cdc.Rectangle(10, 20, 100, 200) or pDC-> Rectangle(10, 20, 100, 200).
Many function arguments and function return values that used to be H
WHAT
EVER
HANDLE
types are now usually pointers to C
Whatever
objects. Thus, for example we might see code like this in Win32.
myFunction(HWND hwnd)
{
HDC hdc;
hdc = GetDC(hwnd)
TextOut(hdc, ...);
...
}
Converting all the H??? to C??? and changing from handles to pointers, we'd expect to see something like the following.
myFunction(CWnd *pWnd)
{
CDC *pDC;
pDC = pWnd->GetDC();
pDC->TextOut(...);
...
}
Or what might happen is that myFunction
is actually a member function of CWnd
now, with code like the following. (Remember that the word 'this' refers to the calling object when used inside the code implementing a class method.)
void CWnd::myFunction()
{
CDC *pDC;
pDC = this->GetDC();
pDC->TextOut(...);
...
}
What is actually inside these new classes like CWnd, CDC, and CBrush
? What are their data members and what are their class methods? The answers are both a relief and an anticlimax.
Most of the C
Whatever
classes have only a single data member, which is a public field of type H
WHATEVER
named m_h
Whatever. Most of the C
Whatever
member methods are just modifications of regular old API functions that used to have a C
Whatever
as the first argument.
The MFC classes are in fact 'shallow wrappers' around regular old Win32 objects. Thus, the only data member of a CWnd
is a public HWND
called m_hWnd. A CBrush's
single data member is a public HANDLE
called m_hObject; normally this is in fact an HBRUSH. (The CBrush
class is a child of the CObject
class.) A CDC
has two data members that are public HDCs
called m_hDC
and m_hAttribDC. (Normally these are equal to each other, but in the case where m_hDC
represents a special metafile or is derived from a printer, m_hAtrribDC
is derived from an onscreen window.)
The good news is that MFC is not very different from Win32, so if you already know Windows programming there's not all that much new to learn. The bad news is that if the Windows programming language was a confused, screwed-up mess, MFC is, if anything, even gnarlier. Like living organisms, real-world computer-languages evolve by successive mutations, rather than being written clean and fresh from the ground up. Every now and then there is a fresh new language, but eventually it too gets all crusty and frobby and tweaked after being out in the world, and (if it is lucky) being heavily used for a few years.
|