Boost logo

Boost :

From: Andrei Alexandrescu (andrewalex_at_[hidden])
Date: 2002-01-12 13:35:10


> But isn't the following as likely:
>
> class Printer {
> };
> void Release(Printer&);
>
> SmartPtr<Printer,...> p(new Printer);
>
> Release(p); // oops, meant Release(*p);

I quote from the MC++D draft manuscript, which also talks about the
overloaded functions issue that you bring up.

<<
However, experience has proven that member functions are not very suitable
for smart pointers. The reason is that the interaction between member
function calls for the smart pointer and member function calls for the
pointed-to object can be extremely confusing.

Consider, for instance, that you have a Printer class with member functions
like Acquire and Release. With Acquire you take ownership of the printer so
that no other application prints to it, and with Release you relinquish
ownership. As you use a smart pointer to Printer, your may notice a strange
syntactical closeness to things that are very far apart semantically.

SmartPtr<Printer> spRes = ...;
spRes->Acquire(); // acquire the printer
... print a document ...
spRes->Release(); // release the printer
spRes.Release(); // release the pointer to the printer

The user of SmartPtr has now access to two totally different worlds: the
world of the pointed-to object members, and the world of the smart pointer
members. A matter of a dot or an arrow thinly separates the two worlds.

On the face of it, C++ does force you to routinely observe certain slight
differences in syntax. A Pascal programmer learning C++ might even feel that
the slight syntactic difference between "&" and "&&" is an abomination. Yet,
C++ programmers don't even blink at it. They are trained by habit to easily
distinguish such syntax matters.

However, smart pointer member functions defeat train by habit. Raw pointers
don't have member functions, so C++ programmers' eyes are not inhabited to
detect and distinguish dot calls from arrow calls. The compiler does a good
job at that - if you use a dot after a raw pointer, the compiler will yield
an error. Therefore, it is easy to imagine, and experience actually proves
it, that even seasoned C++ programmers find it extremely disturbing that
both sp.Release() and sp->Release() compile flag-free, but do very different
things.

The cure is simple. A smart pointer should not use member functions.
SmartPtr uses only nonmember functions. These functions become friends of
the smart pointer class.

Overloaded functions can be just as confusing as member functions of smart
pointers, but there is an important difference. C++ programmers already use
overloaded functions. Overloading is an important part of the C++ language
and is used routinely in library and application development. This means
that C++ programmers do pay attention to differences in function call
syntax - such as Release(*sp) versus Release(sp) - in writing and reviewing
code.

The only functions that necessarily remain members of SmartPtr are the
constructors, the destructor, operator=, operator->, and unary operator*.
All other operations of SmartPtr are provided through named nonmember
functions.

Conclusion

For clarity reasons, SmartPtr does not have any named member function. The
only functions that access the pointee object are GetImpl, GetImplRef, Reset
and Release, which are defined at namespace level.
>>

Andrei

---------------------------------
Check out THE C++ Seminar: 3 Days with 5 Experts
http://www.gotw.ca/cpp_seminar/


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk