From: jbn@lulea.trab.se (Johan Bengtsson)
Newsgroups: comp.lang.c++
Subject: Re: question on REFERENCES
Date: 19 Nov 92 16:10:17 GMT
Organization: Telia Research AB, Aurorum 6, 951 75 Lulea, Sweden
X-Newsreader: Tin 1.1 PL4

swillden@news.ccutah.edu (Shawn Willden) writes:
: jbn@lulea.trab.se (Johan Bengtsson) writes:
: : swillden@news.ccutah.edu (Shawn Willden) writes:
: : : kestes@nswc-wo.nswc.navy.mil (Kent Estes) writes:
: : : : 
: : : : I am working on a function that references objects via id.  Upon finding
: : : : an object, it returns a reference to that object.  My question is :
: : : : if the object is not found, what is the best way to handle this.  If 
: : : : I were returning pointers to objects, all I would have to do is
: : : : return NULL.
: : : 
: : : So return a pointer.  There is no way to return a NULL reference (which
: : : is good, if you have a reference you *know* that it refers to something).
: : : 
: : : If there really is a good reason why you need to return a reference 
: : : instead of a pointer you can declare a special object of the return
: : : type that is always to be returned in case of error and then compare
: : : the address of the returned object with the address of the error 
: : : object (or overload operator== and play games with that).  Usually,
: : : though, you're better off just returning a pointer.
: 
: : A better approach (IMHO) is to register errors in an error variable,
: : which can be a global variable, or one per object (for member functions).
: : The error variable must be explicitly read and reset, before proceeding.
: 
: How is this different from a test for NULL?  The syntax may be slightly
: cleaner but not much is really gained by this approach.

You can often arrange things so that it is safe to attempt quite a few
function calls without checking anything at all (the first error is
safely stored away in a global variable).  If on the average you make
a few (say five) function calls before testing, you get a significant
reduction in the number of error tests, making the real algorithm more
easily recognizable.

: Something must
: still be returned and that something will be invalid if the function 
: fails.  Why is the programmer more likely to remember to test an error
: flag than to test a returned pointer against NULL?

Because the error flag does not go away.  A later check will detect the
error.  Often this is quite acceptable, especially if the error is very
rare.  Also, you can arrange for checking the error flag at program
termination.  Any unhandled error at program exit can then be reported.

Similarly, if an object keeps an internal error state, then its
destructor should check the error state.  If there is an error, the destructor
should report it when the object goes out of scope (the iostream classes
do not do this, but IMHO they should).

This way you can guarantee that no errors pass unnoticed, which is
impossible with the NULL return value approach.

: It can be argued 
: that a program should halt for a serious programmer error such as this
: so as to help the debugging process.

I guess that is what I am saying.  Iostream destructors should IMO
abort the program, if any error bit (except eofbit) is set, or provide
a call-back for this purpose.

[...]
: In the case of a 
: function that returns an object, however, how do you prevent the 
: object returned from being used?  You must include a flag in the object
: that indicates the goodness of the state of the object.  This is 
: reasonable in some cases, not in others, depending on how serious
: the failure is (i.e. maybe the error is such that even an invalid
: object cannot be constructed).

Agreed.  This is sometimes problematic, and you may have to resort to
pointer return values for this reason.

: In addition, it requires all code
: that uses an object to test its goodness and then take appropriate
: action if not.  In many cases it is more convenient to design
: the object such that a given instance is always valid and to just avoid
: creating invalid ones.

Yes, and a nice variation is to return an object that checks the
"goodness" flag itself, and performs safe default actions if it
is an invalid object.  Sometimes this can be programmed by returning
an object of a subclass of the return type (which is a reference),
that overrides virtual functions appropriately.

: Many times beginning C++ programmers get the idea that pointers are
: the `C' way to do things and references are the `C++' way (implying
: that pointers should not be used in `real' C++ code).  I was trying
: to point out to the original poster that pointers have their place.

I'll go along with that...
-- 
--------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden  |
| Johan.Bengtsson@lulea.trab.se; Voice:(+46)92075471; Fax:(+46)92075490  |
--------------------------------------------------------------------------
Newsgroups: comp.lang.c++,comp.std.c++