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.
Comments