From: Kevlin Henney 
To: accu-general@accu.org
Subject: Re: accu-general: Pointers and references the mfc way
Date: Wed, 8 Aug 2001 19:11:57 +0100

>From Kevlin Henney 
Replies will be sent to the list (Reply-to: header set)

In message <21B99E6E6159D411ACD600D0B7AB89FE2ACD8D@MAILUK>, Sebright,
Simon 
>My memory jogged the other day.  To me, one of the benefits of pointers as
>parameter types is that you can see in the calling code quite clearly that
>the function you are calling might be able to modify things you send as
>parameters, rather than the pass-by-reference masquerading as pass-by-value.

This is not at all the case, I'm afraid, and is a common misconception:

        f(&a);

Tells you nothing useful and guaranteed about what might happen to a at
all:

        void f(const A *);

Many programmers struggle with the usage differences between references
and pointers. I suspect that this is in part due to unwitting C
influences (either direct or picked up from their colleagues) and the
(counter)example set by some libraries...

>There's an extra ampersand to type in the code, but the folks at MFC land
>have obviated the need for this.  

... of which MFC is near the top of my list. A dreadful library at
almost any level you care to look at it. It only seems good if you have
never seen how it could or should be done >:-> I believe that MFC
greatly helped Java's uptake beyond the initial Internet hype curve.

>They have things like
>
>class CRect : public RECT
>{
>    ...
>    operator LPRECT();
>    operator LPCRECT() const;
>    ...
>};
>
>which return the this pointer of the CRect object.  Is this laziness gone
>too far?  

Worse than that: it is demonstrably bad design.

In C++ object types are either intended for use directly, without an
emphasised level of indirection (ie values and references), or
indirectly, so that the level of indirection is emphasised (ie identity
or sharing is in some way emphasised). Using one type in both ways is
normally a sign of misunderstanding or a confused design. Not always,
but often. I have found that this serves as a very good early warning
signal that something in the design has been overlooked and needs to be
reconsidered.

In short, CRect is a value concept should not be passed around by
pointer. To do so is a category mistake.

>I much prefer the verbosity of the &, and the full knowledge of
>what is going on ;-)  Is there any established wisdom on this?

Yes, see above ;-)

>I also note a constructor taking a LPCRECT parameter in pseudo-copy
>constructor fashion.  

Aaagh!

>Unfortunately, I have seen unwitting programmers
>construct a rect thus:
>
>CRect rect( NULL );  // Empty rectangle
>
>This compiles, and (usually) doesn't give an empty rectangle, and I am not
>sure why it didn't create an access violation.  Interestingly, they only
>tried to do this because they knew that the default constructor didn't set
>the rectangle to empty!  CRect rect (0,0,0,0) is clearly too much effort ;-)
>Perhaps some lessons for class design?

MS obviously missed the boat completely on the purpose of default
constructors :-( The CRect default ctor leaves the members uninitialised
-- or, as the comment in the source would have it, "random filled",
which demonstrates the problem with comments that are wrong :-(

I think it is lessons in class design that were required. And just in
case anyone gets the idea that I'm slating MS for not predicting the
future of good C++ practice, it is worth pointing out that these
practices were all known when MS strayed into the C++ arena.

>MFC also allows you to cast NULL to a CDC* and call functions on it!  The
>code does something special if (this == NULL).  I guess this is just giving
>you something like a static function, but just seems appalling to me, as it
>won't trap those true error conditions where you've mistakenly got a NULL
>pointer.  Or does anyone have any good reasons for doing this kind of thing?

OK, a CDC is a good example of something to which you would hold a
pointer but not hold by value. However, this is where the good practice
ends. An obvious improvement would have been to wrap up CDC * in a smart
pointer class that handled all the acquisition, sharing and release
issues. That is a design for usage issue. As for the test of this
against null, that is just wrong headed: if !this you have undefined
behaviour, plain and simple. For a vendor to provide defined behaviour
is fine, just so long as this defined behaviour does not go on to become
axiomatic to the framework... which alas, in the case of MFC, it does
:-(

It's been a while since I had to look at the MFC, but thanks for this
posting: it has reminded me of why I used to find browsing it such a
good source of both amusement and counterexamples for coding guidelines,
teaching and consulting >;->
____________________________________________________________

  Kevlin Henney                   phone:  +44 117 942 2990
  http://www.curbralan.com        
  Curbralan: Consultancy + Training + Development + Review
____________________________________________________________

[ACCU mailing list details, see http://accu.org/mailinglists.htm ]


