The Problem
Here's a classic case:
void CMyView::OnPaint()
{
CPaintDC dc(this);
CFont f;
f.CreateFont(...); // parameters not shown
dc.SelectObject(&f);
dc.TextOut(...); // whatever...
} // destructors called here...
|
Looks pretty good, right? Wrong. Look at what happens. The destructors are
called when the context exits. This means that the DC will be freed (in the case
of a CPaintDC this means that ::EndPaint will be called), and the
destructor for the CFont will be called, which means that a ::DeleteObject
will be called. (This has other implications, for example, if you are doing a
CWnd::SetFont call, where the font has to have a lifetime beyond the lifetime
of the variable; see my essay on this topic).
Strictly speaking, there is no specified order in which the destructors are
known to be called. I checked the C++ standard, and although all sorts of issues
are specified in order of execution of destructors, the order in which they are
called for auto variables (that is, ordinary stack variables) seems to
be unspecified. In practice, it appears to be in the inverse order of their declaration,
that is, the font, which is declared after the DC, will be destroyed first, then
the DC will be destroyed. This loses, because when the font is destroyed it is
still selected into the font. (You may suspect that by declaring your fonts before
the CPaintDC will solve this. I would consider this an egregious programming
blunder. For one thing, I'm not sure that deleting the DC first properly sets
the values in the font so that it knows it is no longer in a DC (it could be
selected into several DCs). I would never attempt this.
The proper thing to do is to restore the state of the DC before destroying
it. Typically, this is done by saving the contents of the DC when you do a SelectObject
and then restoring them, for example,
void CMyView::OnPaint()
{
CPaintDC dc(this);
CFont f;
f.CreateFont(...); // parameters not shown
CFont * oldfont = dc.SelectObject(&f);
dc.TextOut(...); // whatever...
dc.SelectObject(oldfont);
} // destructors called here...
|
This will now work properly. When the destructor for the font is called, it
is no longer in a DC, and it will be deleted.