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

139,739

Solution 1

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 pointer" - no need to look up function definition
func2(&mySprite);

Solution 2

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.

Solution 3

I like the reasoning by an article from "cplusplus.com:"

  1. Pass by value when the function does not want to modify the parameter and the value is easy to copy (ints, doubles, char, bool, etc... simple types. std::string, std::vector, and all other STL containers are NOT simple types.)

  2. Pass by const pointer when the value is expensive to copy AND the function does not want to modify the value pointed to AND NULL is a valid, expected value that the function handles.

  3. Pass by non-const pointer when the value is expensive to copy AND the function wants to modify the value pointed to AND NULL is a valid, expected value that the function handles.

  4. Pass by const reference when the value is expensive to copy AND the function does not want to modify the value referred to AND NULL would not be a valid value if a pointer was used instead.

  5. Pass by non-cont reference when the value is expensive to copy AND the function wants to modify the value referred to AND NULL would not be a valid value if a pointer was used instead.

  6. When writing template functions, there isn't a clear-cut answer because there are a few tradeoffs to consider that are beyond the scope of this discussion, but suffice it to say that most template functions take their parameters by value or (const) reference, however because iterator syntax is similar to that of pointers (asterisk to "dereference"), any template function that expects iterators as arguments will also by default accept pointers as well (and not check for NULL since the NULL iterator concept has a different syntax).

http://www.cplusplus.com/articles/z6vU7k9E/

What I take from this is that the major difference between choosing to use a pointer or reference parameter is if NULL is an acceptable value. That's it.

Whether the value is input, output, modifiable etc. should be in the documentation / comments about the function, after all.

Solution 4

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

Solution 5

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!
Share:
139,739

Related videos on Youtube

Matt Pascoe
Author by

Matt Pascoe

I am currently working for CME Group, Inc. as a QA Analyst 2. I run performance testing on their electronic trading platform called Globex. Before working for CME Group, Inc. I was a student at DeVry University studying for my Bachelors of Science in Computer Information Systems. I have worked with C++ for a number of years and am starting to dable in Java here and there. Also, because of the nature of my job I occasionally have to pick up a scripting language to help automate tasks.

Updated on May 14, 2020

Comments

  • Matt Pascoe
    Matt Pascoe about 4 years

    What are the benefits of passing by pointer over passing by reference in C++?

    Lately, I have seen a number of examples that chose passing function arguments by pointers 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);
    
    • Martin York
      Martin York over 5 years
      Don't forget about new to create a pointer and the resulting issues of ownership.
  • Greg Rogers
    Greg Rogers over 15 years
    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).
  • foraidt
    foraidt over 15 years
    @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.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    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.
  • Konrad Rudolph
    Konrad Rudolph over 15 years
    Programs which contain dangling references are not valid C++. Therefore, yes, the code can assume that all references are valid.
  • rmeador
    rmeador over 15 years
    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?
  • Steve Jessop
    Steve Jessop over 15 years
    My stance in that argument is that if the function name makes it totally obvious, without checking the docs, that the param will be modified, then a non-const reference is OK. So personally I'd allow "getDetails(DetailStruct &result)". A pointer there raises the ugly possibility of a NULL input.
  • Steve Jessop
    Steve Jessop over 15 years
    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
    Steve Jessop over 15 years
    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.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas over 15 years
    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.
  • Michael Burr
    Michael Burr over 15 years
    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
    Michael Burr over 15 years
    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.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    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.
  • paercebal
    paercebal over 15 years
    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
    paercebal over 15 years
    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.
  • Mr.Ree
    Mr.Ree over 15 years
    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.
  • Frerich Raabe
    Frerich Raabe over 14 years
    Passing by pointer also raises the 'Is ownership transferred or not?' question. This is not the case with references.
  • Londerson Araújo
    Londerson Araújo over 13 years
    This rule is used in google c++ style guide: google-styleguide.googlecode.com/svn/trunk/…
  • Sylvain Defresne
    Sylvain Defresne over 13 years
    You should probably add that it is cleaner to use pointer when there is ownership transfer, or for registering callbacks (item that must survive the current stack frame).
  • William Pursell
    William Pursell over 12 years
    I disagree with "less chance for bugs". When inspecting the call site and the reader sees "foo( &s )" it is immediately clear that s may be modified. When you read "foo( s )" it is not at all clear if s may be modified. This is a major source of bugs. Perhaps there is less chance of a certain class of bugs, but overall, passing by reference is a huge source of bugs.
  • Gbert90
    Gbert90 about 12 years
    What do you mean by "transparent" ?
  • Michael J. Davenport
    Michael J. Davenport almost 12 years
    @Gbert90, if you see foo(&a) at a call site, you know foo() takes a pointer type. If you see foo(a), you don't know whether it takes a reference.
  • Lightness Races in Orbit
    Lightness Races in Orbit over 11 years
    The second function call used to be annotated func2 passes by reference. Whilst I appreciate that you meant it passes "by reference" from a high-level perspective, implemented by passing a pointer at a code-level perspective, this was very confusing (see stackoverflow.com/questions/13382356/…).
  • Happy Green Kid Naps
    Happy Green Kid Naps over 11 years
    @MichaelJ.Davenport -- that doesn't seem to be the context in which "transparent" is used in Johannes' post.
  • Michael J. Davenport
    Michael J. Davenport over 11 years
    @HappyGreenKidNaps - For my benefit and that of Gbert90, could you elaborate?
  • Happy Green Kid Naps
    Happy Green Kid Naps over 11 years
    @MichaelJ.Davenport -- in your explanation, you suggest "transparent" to mean something along the lines of "obvious that caller is passing a pointer, but not obvious that caller is passing a reference". In Johannes' post, he says "Passing by pointer -- Caller has to take the address -> not transparent" and "Pass by reference -- Caller just passes the object -> transparent" -- which is nearly opposite of what you say. I think Gbert90's question "What do you mean by "transparent"" is still valid.
  • deworde
    deworde over 11 years
    I just don't buy this. Yes, you pass in a pointer, so therefore it must be an output parameter, because what's pointed to can't be const?
  • Jon
    Jon about 11 years
    Transparency means "easy for others to see what actions are performed" (wikipedia). An address-of operator at the call site makes it more transparent to readers that this argument may be modified. I've edited the answer, let's see if the change is accepted.
  • Brandon Ling
    Brandon Ling over 10 years
    Agreed, when I was reading Adam's comment it made sense regarding transparent then when I came to this answer it just confused me because it is backwards. Just giving an outsiders point of view.
  • Madbreaks
    Madbreaks about 10 years
    +1, great answer. Although I will admit I was momentarily confused by your use of -> in your answer...we are talking about pointers here after all. ;)
  • David Stone
    David Stone almost 9 years
    Regardless of what you do with the reference returned, the moment you say *i, your program has undefined behavior. For instance, the compiler can see this code and assume "OK, this code has undefined behavior in all code paths, so this entire function must be unreachable." Then it will assume that all branches that lead to this function are not taken. This is a regularly performed optimization.
  • Jon Wheelock
    Jon Wheelock over 8 years
    Don't we have this passing reference in C? I am using codeblock (mingw) latest version and in that selecting a C project. Still passing by reference (func (int& a)) works. Or is it available in C99 or C11 onwards?
  • Adam Rosenfield
    Adam Rosenfield over 8 years
    @JonWheelock: No, C does not have pass-by-reference at all. func(int& a) is not valid C in any version of the standard. You're probably compiling your files as C++ by accident.
  • Jon Wheelock
    Jon Wheelock over 8 years
    You are right. My file name was .cpp and after changing to .c it gave error.
  • binaryguy
    binaryguy over 8 years
    Yes, for me the NULL related terms are the main concerns here. Thx for quoting..
  • Marco Sulla
    Marco Sulla almost 8 years
    @Jon and the others: I suppose Johannes is talking about interface transparency. It means that he interface masks the complexity of the engine below. If you pass the object directly to the function, the function could get reference or get the value, you can't know, but the interface will remain the same, and it's more simple for the end user. See en.wikipedia.org/wiki/…
  • Earth Engine
    Earth Engine over 7 years
    I think there is a very important exception of those rules: the swap function plays a really important role in the copy-swap idiom, but without declaring its parameters as non-const references, there is no way to do what it intended to do. Using pointers here will be troublesome and breaks its exception-free promise easily.
  • ihodonald
    ihodonald about 7 years
    Don't you mean "null argument?" The parameter is in the function declaration.
  • Arkady Godlin
    Arkady Godlin about 6 years
    as Scott Meyers said in his book "More Effective C++" in page 10 regarding make reference refer to dereferenced null pointer "well this is eveil, pure and simple. The results are undefined and people who write this kind of code should be shunned untill they agree to cease and desist"
  • Ken Jackson
    Ken Jackson over 5 years
    A reference parameter can receive NULL, @AdamRosenfield. Pass it as func(*NULL). Then inside the function, test with if (&x == NULL). I suppose this looks ugly, but the difference between pointer and reference parameters is syntactic sugar.
  • Ken Jackson
    Ken Jackson over 5 years
    No, @DavidStone, the test(*i) statement does not dereference NULL. NULL would only be dereferenced if the function accessed parameter a without the & operator. Perhaps it's assumed to be impossible to pass NULL just to avoid the need to write more bulletproof code.
  • David Stone
    David Stone over 5 years
    @KenJackson: eel.is/c++draft/dcl.ref#5 "A reference shall be initialized to refer to a valid object or function. [ Note: In particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by indirection through a null pointer, which causes undefined behavior."
  • Ken Jackson
    Ken Jackson over 5 years
    The clause of the rule you cited, @DavidStone, seems to limit the propriety of what the programmer may do, rather than specify what the compiler shall do. That's language use, not implementation so one wonders if it's in scope. Regardless, in the example given, NULL is not dereferenced.
  • Adam Rosenfield
    Adam Rosenfield over 5 years
    @KenJackson: Nope, that's Undefined Behavior to dereference a null pointer
  • zjyhjqs
    zjyhjqs about 2 years
    @FrerichRaabe Address could be obtained by reference though the weird semantic is a signal to some dangerous behaviours. But in a well-defined system with RAII it shouldn't be a problem..