Can't seem to think of any useful case where passing by pointer would be preferred over passing by reference, besides the case you pointed out where passing a NULL actually means something.
–
Isaac EMar 31 '10 at 3:53

13 Answers
13

Some like pass-by-pointer better in cases where the object being passed is actually going to be modified. They use pass-by-const-reference when the object is being passed by reference in order to avoid a copy of the object, but will not be changed in the function.

In calling foo vs foo1, it's not apparent from the callers perspective (or a programmer reading the code) that the function can modify testInt without having to look at the signature of the function. Looking at foo2, a reader can easily see that the function may in fact modify the value of testInt because the function is receiving the address of the parameter. Note that this doesn't guarantee the object is actually modified, but that's where being consistent in the use of references vs. pointers helps. In general, if you want to follow this guideline consistently you should always pass const references when you want to avoid copies, and pass by pointer when you want to be able to modify the object.

This is a really good point. You're completely right. As a programmer, I generally wouldn't expect foo1(testInt) to ever modify foo1, and would be kind of surprised if testInt was different after the function call (although I'd figure it out eventually by checking the function definition). But with foo2(&testInt) it's very clear that testInt is available for modification by the function. Excellent answer. I think this is an especially important standard to adopt in situations where tools like autocomplete (which would show the function definition) aren't available, such as in textbooks.
–
CamMar 31 '10 at 4:43

4

You forgot to mention the case when I might want to pass NULL, which is not readily possible with references, whether const or not (another thing you should have brought up in your discussion about expecting things to be or not to be modified by the callee and going the extra step to enforce that, if we're going that route.) :) Most importantly, you forgot to mention the effect of operator= on pointers vs. references!
–
vladrMar 31 '10 at 4:45

5

I find the "you know if it can change" argument to be a weak one. Why are you writing functions so opaque that you need to guess which parameters will be modified? For the initial writing, you must have the definition available to tell you which need to be pointers, so clearly it doesn't do much there.
–
Dennis ZickefooseMar 31 '10 at 6:32

3

I think that the "can't be NULL" property of references is far more important in function signatures than whether the function might modify the referred (or pointed to) parameter. Because the language allows pass by reference to a modifiable object you'll never be completely protected by just a convention. When you use pointers to signal possibly null, it documents exactly where your checks should be. If you use pointers everywhere, you have to document where a pointer must be not-null as a pre-condition (less robust), or add extra null checks to a lot of functions that shouldn't need them.
–
Charles BaileyMar 31 '10 at 7:00

I tend to agree with most of you that I would rather have the "can't be NULL" property of a reference over the "this can't be changed" and this is why I don't follow this convention myself though I can see the benefits of it. Google on the other hand thinks knowing whether the object can be changed is more important as it does follow these conventions. See: google-styleguide.googlecode.com/svn/trunk/…
–
RC.Mar 31 '10 at 12:02

References are usually preferred over
pointers whenever you don't need
"reseating". This usually means that
references are most useful in a
class's public interface. References
typically appear on the skin of an
object, and pointers on the inside.

The exception to the above is where a
function's parameter or return value
needs a "sentinel" reference — a
reference that does not refer to an
object. This is usually best done by
returning/taking a pointer, and giving
the NULL pointer this special
significance (references should always
alias objects, not a dereferenced NULL
pointer).

Note: Old line C programmers sometimes
don't like references since they
provide reference semantics that isn't
explicit in the caller's code. After
some C++ experience, however, one
quickly realizes this is a form of
information hiding, which is an asset
rather than a liability. E.g.,
programmers should write code in the
language of the problem rather than
the language of the machine.

You have many situations in real world programming wherein a parameter does not exist or is invalid and this can depend on runtime semantics of the code. In such situations you can use NULL (0) to signal this state. Apart from this,

A pointer can be re-assigned to a new
state. A reference cannot. This is
desirable in some situations.

A pointer helps transfer owner-ship
semantics. This is especially useful
in multi-threaded environment if the
parameter-state is used to execute in
a separate thread and you do not
usually poll till the thread has
exited.

Although if enough time is spent designing the code properly, these situations can be avoided; in practice it is not possible everytime.

In addition to some other answers about ownership semantics (especially factory functions).

While not a technical reason, a common style guide requirement is that any parameters that may be modified should be passed by pointer. This makes it obvious at the callsite that the object may be modified.

Or don't pass it at all. Oftentimes, you can simply provide an overload that only takes the useful parameters.
–
Dennis ZickefooseMar 31 '10 at 7:08

@Dennis: yes and no, you may want to deal with both cases in a uniform manner: you are iterating over a container of pointers, or passing a pointer returned by another function...
–
Matthieu M.Mar 31 '10 at 7:21

In C++ there's little to no need to pass by pointer in most cases. You should consider first the alternatives: templates, (const) references, containers, strings and smart pointers. That said, if you must support legacy code then you'll need to use pointers. If your compiler is minimalistic (embedded systems, for example) you'll need pointers. If you need to talk to a C library (a system library of some sort for a very particular driver you're working with?) then you'll need to work with pointers. If you want deal with very particular offsets of memory then you'll need pointers.

In C pointers are first-class citizens, they are way too fundamental to think about eliminating them.

This isn't specifically argument passing, but it does affect argument passing.

You can have a container/aggregate of pointers, but not of references. Although references are polymorphic, only pointers support the "update" operation which is essential to container use (especially since you can't yet initialize references en masse, not sure if C++0x aggregate initializers will change that).

So if you have a container full of pointers, you will usually end up managing it with functions that accept pointers rather than references.

+1 for an interesting point in other uses of pointers, even if while internals need to be pointers, interfaces can still be references (there is no reason not to have a reference in the method interface and store the address of the object inside a container or member attribute).
–
David Rodríguez - dribeasMar 31 '10 at 7:15

Look up the Boost.Pointer Containers library. I only wish a version without ownership existed for the "index" case :/
–
Matthieu M.Mar 31 '10 at 7:19

@dribeas: Thank you for illustrating another case when IMO references should absolutely NOT be used: when a pointer or reference to the parameter is going to be kept longer than the duration of the function call. Pass-by-reference in such cases encourages using a local variable (or even worse, const references can bind to temporaries). If you're going to store the address of the argument in whatever way, make the caller pass you that address explicitly.
–
Ben VoigtMar 31 '10 at 13:17

I would bash on the head anyone deleting my pointer! If you wish to manipulate memory, make it clear in the interface that you will take ownership by using a auto_ptr or better a unique_ptr.
–
Matthieu M.Mar 31 '10 at 7:18

If I need to pass an array of objects, I need to pass by pointer. Arrays aren't always stored in std::vector<>.

Other than that, pass by pointer allows NULL and pass by reference doesn't, so I use that distinction as a contract much like NULL vs. NOT NULL columns in SQL, and loosely like "function returning bool: true = succeeded and false = failed, vs. function returning int: return value is a result code where 0 = success and others are failures".

Eh, dynamic arrays should always be stored in a vector. That said, that's kind of a loophole in the question: If you were planning on modifying the pointer, would you do a double pointer or reference? (The question isn't about a pointer in the argument list, it's about a pointer versus reference when you need to modify it within the function. The pointer in your case is just a by-product.)
–
GManNickGMar 31 '10 at 4:14

I've written a huge amount of code that works with data not stored in vector, and shouldn't be. When you get a packet of data, you want to operate on that data directly and not waste time resizing a vector and copying the data to it. Also, you can do transpose operations or strided (every nth element) operations trivially with pointers. BLAS-type code will never use vector inherently; you'll have lots of &vec[0] code if you try. (Forcing everything to live in vectors is one reason C++ is unfairly accused of causing code bloat. Don't use it if you don't need it.)
–
Mike D.Mar 31 '10 at 4:23

@Mike: How about: vector<char> buffer(bufferSize); recv(&buffer[0], buffer.size()); etc.? If you're copying data into a vector, why not just fill it into the vector directly? Of course, the recommendation of "use vector's" is gone when using older style code. In new code, I've never come across a reason to handle raw memory. (Seriously, a vector just does that but makes sure it's deleted is all. There's nothing you can do with raw memory allocation that you can't do with vector.)
–
GManNickGMar 31 '10 at 4:30

1

@GMan yes there is a reason. I can allocate my buffer on the stack without killing myself with heap allocation/deallocation (non-trivial, bad locality for caches, and possibly involving locking.)
–
vladrMar 31 '10 at 4:53