tags:

views:

623

answers:

12

I can imagine one case, in which the input parameter could be NULL so that pass-by-pointer is preferred but not pass-by-reference?

Can anybody add more cases?

+1  A: 

When dealing with raw memory (for example if creating your own memory pool), you'd want to use a pointer. But you're right, in normal code the only use for a pointer is an optional parameter.

rlbond
+22  A: 

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 illustration, take the following functions:

int foo(int x);
int foo1(int &x);
int foo2(int *x);

Now in the code, I do the following:

int testInt = 0;

foo(testInt);   // can't modify testInt
foo1(testInt);  // can modify testInt

foo2(&testInt); // can modify testInt

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.

RC
Cam
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!
vladr
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 Zickefoose
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 Bailey
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: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments
RC
Additionally, the argument that it makes the caller's code clearer only holds true for one level of functions. IOW, if testInt was already an input pointer parameter, you'd simply call foo2(testInt), and now you're back to looking at the surrounding code to understand the meaning, anyway.
Ben
@Ben - you still know that your passing a pointer and this is mutable. If the 2nd tier function was taking a const ref you would be calling it like: foo2(*testInt) and could easily see it could not be modified by the second tier function.
RC
+1  A: 

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.

wilhelmtell
A: 

A function could, conceivably, be written to do something with the memory, such as reallocating it to make it larger (returning the new pointer).

Jon Reid
Wouldn't that be a case for a reference to a pointer?
Travis Gockel
+1  A: 

Anytime you pass a function pointer. Or if you have a "reset" method a la auto_ptr.

wheaties
A: 

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".

Mike D.
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.)
GMan
Mike D.
@Mike: How about: `vector<char> buffer(bufferSize); recv(` 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.)
GMan
@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.)
vladr
@Vlad: Where did statically allocated arrays come into play? Note my **first words** : "Eh, dynamic arrays should...". Obviously if a static array will do, use one.
GMan
@Vlad: Yes, I meant "statically sized". The size must be a compile-constant, aka static (in contrast with dynamic). Not that the array itself has to be static. `bufferSize` in your code must be a compile-time constant. In any case, this is all completely irrelevant. The discussion is about vector versus manual allocation.
GMan
This is a question specifically related to programming in C++. Why on earth would one read up on C99?
Dennis Zickefoose
Because, pragmatically speaking, GCC for instance supports it, waiting for http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2648.html
vladr
@GMan: That would have worked before, but we managed to save a couple copies by passing the data via shared memory and just pushing a few bytes of transaction info through the socket (mostly so we don't have to write event-passing drivers or break out semaphores, which have their own complications). Since the algorithms expected pointers, they Just Worked without modification. Had we used vectors, we would have had to mass-change APIs or write a vector-like object that gets initialized with a pointer to preexisting storage. Converting vectors to pointers is easy, the other way not so much.
Mike D.
@Dennis: To know what you're missing out on? Like the `struct foo bar = { .f1=10, .f2=20, ... };` construct. Or never having to use `alloca` again.
Mike D.
+5  A: 

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.

Abhay
+1 for `NULL` and assignment!
vladr
+2  A: 

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.

void Operate(const int &input_arg, int *output_arg) {
  *output_arg = input_arg + 1;
}

int main() {
  int input_arg = 5;
  int output_arg;
  Foo(input_arg, &output_arg);  // Passing address, will be modified!
}
Stephen
Would you generalize this stylistic guideline to *all* parameters, or mostly to POD-type parameters?
vladr
Definitely all parameters.
Stephen
+1  A: 

When you need to manipulate (e.g. resize, reassign, etc.) the address being pointed to by the pointer inside the function, then you need a pointer.

For example:


void f( int* p )
{
    // ..

    delete []p;
    p = new int[ size ];

    //...
}

Unlike references, values of pointers can be changed and manipulated.

jasonline
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.
+1  A: 

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.

Ben Voigt
+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 - dribeas
Look up the `Boost.Pointer Containers` library. I only wish a version without ownership existed for the "index" case :/
Matthieu M.
@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 Voigt
+12  A: 

The C++ FAQ Lite has a very good answer for this question:

Use references when you can, and pointers when you have to.

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.

Andreas Bonini
+1: Good and appropriate quote.
ArunSaha
+3  A: 

Rule number one for this: If NULL is a valid value for the function parameter in the context of the function, then pass it as pointer, otherwise pass it as reference.

Rationale, if it cannot (should not!) ever be NULL, then don't put yourself through the trouble of checking for NULL or risking problems due to it being NULL.

Johann Gerell
Or don't pass it at all. Oftentimes, you can simply provide an overload that only takes the useful parameters.
Dennis Zickefoose
@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.