Re: virtual CDialog base class - How to ??
On Tue, 04 Mar 2008 19:34:28 -0800, Henryk Birecki
<soaringpilot@sbcglobal.net> wrote:
Right, but my problem is not with multiple levels but multiple
"instances". What I really want is a class of that would be declared
as:
class CMySpecialPropertyPage: public CPropertyPage, CMyModifiedDialog
where CMyModifiedDialog is itself derived from CDialog, just as
CPropertyPage is. In this case there would be confusion which copy of
CDialog functions need to be used. That is why I wanted to have
class CMyModifiedDialog: virtual public CDialog , but this does not
appear to work.
Joe is correct; multiple inheritance of CObject-derived classes is at best
very hard to make work, much less virtual inheritance of CWnd. Borland's
OWL library supported this, and OWL used MI to compose view classes from
TWnd and TView classes. MFC wants to do the same thing, but it can't, due
to its design precluding MI, but that didn't stop MFC from sort of faking
it in classes such as CTreeView, which is derived from CCtrlView and thus
CWnd. It provides the GetTreeCtrl function for those times when you want to
treat it as a CTreeCtrl, and this function casts *this to CTreeCtrl&, which
is an evil reinterpret_cast under the hood. It mostly works due to
compatible object layouts, but you can still expose the fraud with RTTI.
It's also very limited, as you can't substitute your own CTreeCtrl-derived
class.
All that said, it is possible to derive from CWnd and one of your own
classes that isn't derived from CObject. For example, CMyModifiedDialog has
some behavior you want to share in derived classes. You can abstract that
behavior out into another class, say, CDlgBehavior, which uses virtual
functions instead of the message map. Then the derived classes would have
to catch the messages CDlgBehavior wants and call its corresponding handler
functions. Your hierarchy would look like:
// NB: It is OK to use MI with CObject-derived classes, provided there is only
// one of them, and it is the left-most in the base class list.
class CMyModifiedDialog : public CDialog, CDlgBehavior
{
....
};
class CMySpecialPropertyPage : public CPropertyPage, CDlgBehavior
{
....
};
If you want to modify CDlgBehavior's behavior, you can override its virtual
functions, of course, and this avoids the deficiency in the CTreeView
design mentioned earlier. Of course, it's a pain that each derived class
has to forward messages to CDlgBehavior, but at least you can share the
behavior. You can mitigate it somewhat for families of related classes by
deriving some classes to serve as intermediate base classes that do the
forwarding, e.g.
class CDlgWithBehavior : public CDialog, CDlgBehavior
{
....
};
class CPropertyPageWithBehavior : public CPropertyPage, CDlgBehavior
{
....
};
Then you can derive all the CMyModifiedDialogs you want from
CDlgWithBehavior and vice versa for property pages.
Oh, and so that CDlgBehavior can call CDialog functions, do this:
#pragma warning(disable : 4355)
class CDlgBehavior
{
public:
CDlgBehavior(CDialog& dlg, params)
: m_dlg(dlg)
{
}
private:
CDialog& m_dlg;
};
class CMyModifiedDialog : public CDialog, CDlgBehavior
{
public:
CMyModifiedDialog(params)
: CDialog(params1),
CDlgBehavior(*this, params2)
{
}
};
The only catch, and it's a tiny one, is that CDlgBehavior must not call any
function in CMyModifiedDialog before the construction of CMyModifiedDialog
is complete. (That's what the C4355 was about.)
For even more flexibility, forget about using inheritance. Instead,
dynamically create CDlgBehavior-derived objects, and store pointers to them
in CMyModifiedDialog, CMySpecialPropertyPage, etc. That would be an
application of the "Strategy" design pattern.
--
Doug Harrison
Visual C++ MVP