shuisheng said:
Dear All,
The problem of choosing pointer or reference is always confusing me.
Would you please give me some suggestion on it. I appreciate your kind
help.
For example, I'd like to convert a string to a integer number.
bool Convert(const string& str, int* pData);
bool Convert(const string& str, int& data);
which one is better? Here bool is to show if the conversion is
successful.
There are two schools of thought on this. The "C++ way" is to use a
reference. It simplifiys the syntax a bit, makes it harder to pass a
completely bogus pointer, etc. However, there is a school of thought
that says that you should use pointers for "out" parameters, because
it's self-documenting *at the call sites* (without even looking at the
function signature) that the function could change the arguments. The
argument goes that if you see "foo(arg)", you can't tell whether foo
will change its argument, but if you see "foo(&arg)", you know it can.
I don't fully buy into this argument, but it's at least fairly widely
held. It's my suspicion that an argument somewhat along these lines is
why Java doesn't have pass-by-reference. C# solves this problem by
making you specify at call sites that something is being passed by
reference. For instance, if foo is declared as 'void foo(int ref a)',
when calling it you have to say 'foo(ref x)'. I think this is probably
a reasonable compromise, and might be better than either the C or the
C++ approach. I've somewhat taken up the habit of doing something
similar in C++ at call sites of functions that take reference
parameters to use them rather than const refs for speed, and write
foo(*& x). (This isn't always safe though.)
So I would summarize:
Arguments in favor of pointers:
- You can't pass by reference if you want it to be optional
- It's self-documenting at call-sites potential behavior of the
function
Arguments in favor of references:
- It does a slightly better job at enforcing that you give it a valid
object, because it decreases the amount you have to deal with pointers
at all
- The function itself is self-documenting that you must pass a valid
object, whereas you have to rely on either reading code or written
documentation to tell if you can pass a null pointer to a function that
takes pointers
- Somewhat cleaner syntax (IMO)
- It's the "C++ way"
In summary, I would go with what one of the other responses said, and
say use pointers if passing NULL makes sense for an optional parameter,
and use references if you have to be provided a valid object.
(Also note that if you're passing for efficiency -- you should
definitiely always use a const reference rather than a pointer to
const. Here you don't WANT to give the impression that it can be
modified, 'cause it can't.)
Another example.
class Shape {};
class Sphere: public Shape {};
class Cube: public Shape {};
class Union: public Shape
{
private:
Shape* pShape1, pShape2;
// Or Shape& shape1, shape2;
public:
// Constructor.
Union(const Shape* pShape1, const Shape* pShape2); // or using
references
This falls under my "passing for efficiency use references" rule I
think
};
Shape* UnionOperation(const Shape* pShape1, const Shape* pShape2);
or
Shape& UnionOperation(const Shape& shape1, const Shape& shape2);
or
Shape* UnionOperation(const Shape& shape1, const Shape& shape2);
Unless the UnionOperation is acting like a += operator and mutating one
of the elements then returning the new value, you almost hve to return
neither a reference nor a pointer. I can tell though that it's not
acting as that because both arguments are const.
It's very rare that you should return a reference. The only common
exceptions I can think of is containers returning references to
elements and smart pointers returning a reference to elements, if
that's how you want your smart pointer to work. (Even in the second
case, I don't know that I know all the implications of returning by
reference; it could be that that would cause subtle problems. Providing
access to the referent by pointer is more common.)
For instance, the following code:
int& foo() { int a; return a; }
is wrong, because the return value can never be accessed. (Automatic
storage goes away as soon as the function returns.
The same problem is in the following code:
int* foo() { int a; return &a; }
The pointer this returns is necessarily invalid.
To return a pointer or reference from a function, you need to refer to
data that it gets from somewhere else. A possibly incomplete list would
be:
-- For member functions of a class, data stored in the object (this
would be like the containers or function pointers)
-- Global data
-- Static data
-- Parameters (this is like most of the str___ functions from the std
library; they return one of the parameters passed in)
-- Heap allocated storage
The last one deserves more comment. You can write this:
int* foo() { return new int; }
or even this (I think):
int& foo() { return *(new int); }
but both are probably a bad idea at best.
In the first case, the caller of foo has to call delete on whatever
pointer is returned. This isn't enforced; it's just an implicit part of
the contract. And it's of course prone to forgetting to return, etc.
And finally you have to document what you need to use to delete it --
maybe the return from foo() was allocated with malloc, maybe new, maybe
some other allocator. And you have to pair the correct deallocation
routine. "Callee-allocated buffers" are non too uncommon, at least in C
code, so their use is not totally unresonable, but it's probably
usually better to ask the user to allocate a buffer and pass a pointer
or reference to it which the function can then fill in.
The second case is even worse because it's possible to write stuff like
"int x=foo();" where you don't even have to realize that you're dealing
with an int& return. Even if you did realize that, it's so non-standard
(I mean standard here not in the sense of ISO 14882 but just what is
used in practice) that it would totally throw anyone using it off.
Evan