35

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?

jamesdlin
  • 81,374
  • 13
  • 159
  • 204
skydoor
  • 25,218
  • 52
  • 147
  • 201
  • 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 E Mar 31 '10 at 03:53
  • 1
    possible duplicate of [How to pass objects to functions in C++?](http://stackoverflow.com/questions/2139224/how-to-pass-objects-to-functions-in-c) – sbi Sep 01 '10 at 21:27

13 Answers13

40

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.
  • 27,409
  • 9
  • 73
  • 93
  • 6
    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. – Cam Mar 31 '10 at 04: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! – vladr Mar 31 '10 at 04:45
  • 6
    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 Mar 31 '10 at 06: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. – CB Bailey Mar 31 '10 at 07: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: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments – RC. Mar 31 '10 at 12:02
  • 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 Apr 06 '10 at 22:45
  • @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. Apr 11 '10 at 13:15
25

The C++ FAQ 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.

Frxstrem
  • 38,761
  • 9
  • 79
  • 119
Andreas Bonini
  • 44,018
  • 30
  • 122
  • 156
6

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
  • 7,092
  • 3
  • 36
  • 50
4

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
  • 47,994
  • 7
  • 61
  • 70
  • Would you generalize this stylistic guideline to *all* parameters, or mostly to POD-type parameters? – vladr Mar 31 '10 at 04:56
4

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
  • 24,991
  • 10
  • 72
  • 122
  • 1
    Or don't pass it at all. Oftentimes, you can simply provide an overload that only takes the useful parameters. – Dennis Zickefoose Mar 31 '10 at 07: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 07:21
2

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
  • 277,958
  • 43
  • 419
  • 720
  • +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 Mar 31 '10 at 07: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 07: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 Voigt Mar 31 '10 at 13:17
  • You could still obtain the address of the referred parameters (which is also the address of the reference itself) or you could use std::reference_wrapper. – Matthias Mar 01 '18 at 10:05
  • @Matthias: Nope, there's no way to get the address of the reference. It's transparent, applying the `&` operator will perform it on the target object. And while you can use reference parameters to find the target's address, I still strongly feel that the guidance in my previous comment of " If you're going to store the address of the argument in whatever way, make the caller pass you that address explicitly" is correct. – Ben Voigt Mar 01 '18 at 13:48
  • @Ben Voigt I didn't mean the address of the reference as in the address of the underneath pointer implementation of the compiler, but rather that the reference's address should be thought of as the referee's address due to the transparency of references. So you can transform references to a referee always to a pointer to that referee. – Matthias Mar 01 '18 at 14:20
  • @Matthias: I'm not sure how "address of the reference itself" can ever mean anything except the reference itself. And it is not good to conflate the reference with the referee / target... they have different lifetimes and occupy different storage. `operator&` (unless overloaded) or `std::addressof` apply to the target object, there's no way to apply them to the reference. So I agree that you can get the target's address using a reference parameter, I just disagree that it is a good idea to store a pointer obtained that way, because it's hidden from the caller. – Ben Voigt Mar 01 '18 at 16:05
  • @BenVoigt Apparently, some people disagree and added std::reference_wrapper to the standard which exactly does that: convert the reference to a pointer and store that pointer. – Matthias Mar 01 '18 at 16:17
  • @Matthias: `std::reference_wrapper` is not transparent, so it's not the same. – Ben Voigt Mar 01 '18 at 16:19
1

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
  • 65,341
  • 56
  • 178
  • 228
1

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
  • 57,473
  • 20
  • 96
  • 131
1

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

wheaties
  • 35,646
  • 15
  • 94
  • 131
1

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
  • 8,646
  • 19
  • 59
  • 80
  • 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 07:18
0

Google seems to have a strong opinion on this, and I tend to agree:

http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Reference_Arguments

It'sPete
  • 5,083
  • 8
  • 39
  • 72
0

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
  • 20,545
  • 2
  • 64
  • 95
0

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.
  • 1,457
  • 7
  • 7
  • 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.) – GManNickG Mar 31 '10 at 04: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 `vector`s 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 04:23
  • @Mike: How about: `vector 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.) – GManNickG Mar 31 '10 at 04: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.) – vladr Mar 31 '10 at 04:53
  • @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. – GManNickG Mar 31 '10 at 05:18
  • @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. – GManNickG Mar 31 '10 at 06:51
  • This is a question specifically related to programming in C++. Why on earth would one read up on C99? – Dennis Zickefoose Mar 31 '10 at 07:03
  • Because, pragmatically speaking, GCC for instance supports it, waiting for http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2648.html – vladr Mar 31 '10 at 07:11
  • @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. Apr 01 '10 at 04:12
  • @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. Apr 01 '10 at 04:14