views:

3077

answers:

6

Are there benefits of passing by pointer over passing by reference in C++?

Lately, I have seen a number of examples that pass the a pointer instead of passing by reference. Are there benefits to doing this?

Example:

func(SPRITE *x);

with a call of

func(&mySprite);

vs.

func(SPRITE &x);

with a call of

func(mySprite);
+20  A: 

Passing by pointer

  • Caller has to take the address -> not transparent
  • A 0 value can be provided to mean nothing. This can be used to provide optional arguments.

Pass by reference

  • Caller just passes the object -> transparent. Has to be used for operator overloading, since overloading for pointer types is not possible (pointers are builtin types). So you can't do string s = &str1 + &str2; using pointers.
  • No 0 values possible -> Called function doesn't have to check for them
  • Reference to const also accepts temporaries: void f(const T& t); ... f(T(a, b, c));, pointers cannot be used like that since you cannot take the address of a temporary.
  • Last but not least, references are easier to use -> less chance for bugs.
Johannes Schaub - litb
Good points for either sides of the argument. As far as I am concerned, your answer is THE answer. +1.
paercebal
Passing by pointer also raises the 'Is ownership transferred or not?' question. This is not the case with references.
Frerich Raabe
+25  A: 

A pointer can receive a NULL parameter, a reference parameter can not. If there's ever a chance that you could want to pass "no object", then use a pointer instead of a reference.

Also, passing by pointer allows you to explicitly see at the call site whether the object is passed by value or by reference:

// Is mySprite passed by value or by reference?  You can't tell 
// without looking at the definition of func()
func(mySprite);

// func2 passes by reference - no need to look up function definition
func2(&mySprite);
Adam Rosenfield
Great answer - it would be even better with a quick edit of the first code comment such that it doesn't overflow and cause a scrollbar. Yes, I'm pedantic!
Bobby Jack
Incomplete answer. Using pointers won't authorize uses of temporary/promoted objects, nor the use of pointed object as stack-like objects. And it will suggest that the argument can be NULL when, most of the time, a NULL value should be forbidden. Read litb's answer for a complete answer.
paercebal
+3  A: 

Not really. Internally, passing by reference is performed by essentially passing the address of the referenced object. So, there really aren't any efficiency gains to be had by passing a pointer.

Passing by reference does have one benefit, however. You are guaranteed to have an instance of whatever object/type that is being passed in. If you pass in a pointer, then you run the risk of receiving a NULL pointer. By using pass-by-reference, you are pushing an implicit NULL-check up one level to the caller of your function.

Brian
Thats both an advantage and a disadvantage. Many APIs use NULL pointers to mean something useful (ie NULL timespec wait forever, while the value means wait that long).
Greg Rogers
sometimes you can even gain performance by using references, since they don't need to take any storage and don't have any addresses assigned for themself. no indirection required.
Johannes Schaub - litb
+1  A: 

@Brian:

I don't want to be nit-picking but: I would not say one is guaranteed to get an instance when getting a reference. Dangling references are still possible if the caller of a function de-references a dangling pointer, which the callee cannot know.

mxp
Programs which contain dangling references are not valid C++. Therefore, yes, the code *can* assume that all references are valid.
Konrad Rudolph
I can definitely dereference a null pointer and the compiler won't be able to tell... if the compiler can't tell it's "invalid C++", is it really invalid?
rmeador
Yes, it's invalid even if the compiler can't tell. Merely creating a dangling reference is specified to result in undefined behaviour. You can (well, you have no choice really) write code on the assumption that the program is in a defined state.
Steve Jessop
So to be nit-picky one could say "assuming that the program state isn't already FUBARed, you're guaranteed an instance", instead of "you're guaranteed an instance". A dangling reference is an example of a FUBARed program state, but it should go without saying that your caller obeys the rules.
Steve Jessop
+10  A: 

Allen Holub's "Enough Rope to Shoot Yourself in the Foot" lists the following 2 rules:

120. Reference arguments should always be `const`
121. Never use references as outputs, use pointers

He lists several reasons why references were added to C++:

  • they are necessary to define copy constructors
  • they are necessary for operator overloads
  • const references allow you to have pass-by-value semantics while avoiding a copy

His main point is that references should not be used as 'output' parameters because at the call site there's no indication of whether the parameter is a reference or a value parameter. So his rule is to only use const references as arguments.

Personally, I think this is a good rule of thumb as it makes it more clear when a parameter is an output parameter or not. However, while I personally agree with this in general, I do allow myself to be swayed by the opinions of others on my team if they argue for output parameters as references (some developers like them immensely).

Michael Burr
Steve Jessop
@onebyone - that seems like another good rule of thumb
Michael Burr
This is misleading. Even if some do not like references, they are a important part of the language and should be used as that. This line of reasoning is like saying don't use templates you can always use containers of void* to store any type. Read answer by litb.
David Rodríguez - dribeas
I don't see how this is misleading - there are times when references are required, and there are times when best practices might suggest not using them even if you could. The same can be said for any feature of the language - inheritance, non-member friends, operator overloading, MI, etc...
Michael Burr
By the way, I agree that litb's answer is very good, and is certainly more comprehensive than this one - I just elected to focus on discussing a rationale for avoiding using references as output parameters.
Michael Burr
+2  A: 

Clarifications to the preceding posts:


References are NOT a guarantee of getting a non-null pointer. (Though we often treat them as such.)

While horrifically bad code, as in take you out behind the woodshed bad code, the following will compile & run: (At least under my compiler.)

bool test( int & a)
{
  return (&a) == (int *) NULL;
}

int
main()
{
  int * i = (int *)NULL;
  cout << ( test(*i) ) << endl;
};


The real issue I have with references lies with other programmers, henceforth termed IDIOTS, who allocate in the constructor, deallocate in the destructor, and fail to supply a copy constructor or operator=().

Suddenly there's a world of difference between foo(BAR bar) and foo(BAR & bar). (Automatic bitwise copy operation gets invoked. Deallocation in destructor gets invoked twice.)

Thankfully modern compilers will pick up this double-deallocation of the same pointer. 15 years ago, they didn't. (Under gcc/g++, use *setenv MALLOC_CHECK_ 0* to revisit the old ways.) Resulting, under DEC UNIX, in the same memory being allocated to two different objects. Lots of debugging fun there...


More practically:

  • References hide that you are changing data stored someplace else.
  • It's easy to confuse a Reference with a Copied object.
  • Pointers make it obvious!
Mr.Ree
that's not the problem of the function or references. you are breaking language rules. dereferencing a null pointer by itself is already undefined behavior. "References are NOT a guarantee of getting a non-null pointer.": the standard itself says they are. other ways constitute undefined behavior.
Johannes Schaub - litb
I agree with litb. While true, the code you are showing us is more sabotage than anything else. There are ways to sabotage anything, including both the "reference" and "pointer" notations.
paercebal
I did say it was "take you out behind the woodshed bad code"! In the same vein, you can also have i=new FOO; delete i; test(*i); Another (unfortunately common) dangling pointer/reference occurrence.
Mr.Ree
It's actually not *dereferencing* NULL that's the problem, but rather *USING* that dereferenced (null) object. As such, there really is no difference (other than syntax) between pointers and references from a language-implementation perspective. It's the users who have different expectations.
Mr.Ree