#1
  1. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    2
    Rep Power
    0

    Returning a reference to an object


    I'm just re-freshing my old C++ knowledge having been emersed almost exclusively in Java/J2EE for the past 5 years. So I picked up a second hand copy of the old C++ bible 'Effective C++' by Scott Meyers and item 31 got me thinking.

    See: http://www.eso.org/projects/itpub/sq...EC/EI31_FR.HTM

    Never return a reference to a local object or to a dereferenced pointer initialized by new within a function
    I totally agree with what Mr Meyer's is saying - a local object is going to be invalid by the time the reference is returned and a new object is likely to lead to memory leaks. However unlike most of the other items in the book, an appropriate solution is not really offered.

    In Java this is not an issue because the garbage collector takes care of things. However the only option I see in C++ is to create a new object before calling the other object's member function. The new object would be passed into the function which would then set the appropriate state on the new object before returning.

    This seems to break the rules of encapsulation to me because the calling object seems to require too much knowledge about what the called function is going to need to create/set. Does anyone see a more straight-forward solution? In Java, the called method's return type would ideally be an interface (abstract class) not a class (concrete class) thus defering the choice of the new object's real type to the internels of the method which is being called.

    How will I be able to implement the GoF 'abstract factory' and 'factory method' design patterns if the member function I call can't create the object for me? Or am I missing something obvious?

    By the way - I've just discovered this discussion forum - if there's a more appropriate forum out there, which I should use, please point me at the appropriate url. :)

    Thanks

    Paul
    Last edited by pdonut; July 1st, 2003 at 10:24 AM.
  2. #2
  3. Left due to despotic ad-min
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Jun 2003
    Posts
    1,044
    Rep Power
    13
    You raise a few issues here.

    The issue with returning a reference or pointer to a local variable is that the variable will pass out of scope when the function returns, so you have a reference to an object that formally no longer exists. If you use that reference, the result is formally undefined behaviour. The issue with returning a reference to an object created by operator new is that you will (eventually) need to obtain it's address to delete it or face the prospect of a memory leak. In other words, you gain nothing compared with returning a pointer.

    There are a number of practical solutions in C++. Applicability depends on what you're doing. Options include;

    1) Return a reference to a global object that is guaranteed to exist for as long as you need the reference.

    2) [Applicable if the function returning a reference is a member function of some class]. Return a reference to a data member. In some circumstances, this violates encapsulation (by allowing unchecked modification of private data) but this is actually what is done in the stl (indirectly) using iterator types.

    3) Return a reference to self (eg return *this;) Often used in things like user-defined assignment operators.

    4) Return data by value rather than by reference. The compiler guarantees that a copy of the object will exist until the next sequence point in the caller. This allows the caller to copy the data to somewhere it can use it. With a decent compiler and optimiser, the number of temporary objects created and deleted can be minimised (this is something specifically allowed by the C++ standard).

    I'm sure there are other approaches that I haven't thought of in the last minute or two :-)

    Most abstract factory methods (or objects) as implemented in C++ create objects dynamically (using operator new). They may return raw pointers (in which case the caller is responsible for avoiding leaks) or may return a smart pointer type (eg std::auto_ptr<Object>) that will manage object lifetime for you.
  4. #3
  5. No Profile Picture
    Junior Member
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jul 2003
    Posts
    2
    Rep Power
    0
    Thanks for the good detailed reply - this certainly helps me. :)

    Also, thinking a bit more about this, apart from factory pattern situations, I should ideally be aiming for any objects created by a parent object to be tied to the lifecycle (and hence ultimate destruction) of the parent object, to achieve true encapsulation. In cases where this doesn't apply, passing objects back 'by value' is probably the cleanest way of achieving separation.

    Cheers

    Paul
  6. #4
  7. Left due to despotic ad-min
    Devshed Beginner (1000 - 1499 posts)

    Join Date
    Jun 2003
    Posts
    1,044
    Rep Power
    13
    You're mixing up the ideas of encapsulation and managing object lifetime. Encapsulation is, in rough terms, about grouping behaviour in a controlled manner (eg ensuring attributes can only be changed by defined mechanisms).

    Returning objects by value is one technique that helps achieve encapsulation, but it potentially comes with a performance hit (copying the return value so it can be returned). An alternate way is to return a const reference (a reference that doesn't allow the affected entity to be changed).

    Object lifetime, which is something that needs to be controlled if you want your software to be robust, not have memory leaks, etc etc is something else entirely. There are various techniques for managing object lifetime. One *very* useful one is to use the RAII (Resource Acquisition Is Initialisation) techniques. Essentially a resource is something that may be allocated and deallocated dynamically, and explicitly, by your program, such as dynamic memory, file handles, critical section objects, mutexes, .... The RAII technique is essentially about having resources allocated in constructors and deallocated in destructors.

IMN logo majestic logo threadwatch logo seochat tools logo