Where should I prefer pass-by-reference or pass-by-value?
Asked Answered
S

8

34

In what circumstances should I prefer pass-by-reference? Pass-by-value?

Scar answered 13/2, 2011 at 19:29 Comment(0)
H
51

There are four main cases where you should use pass-by-reference over pass-by-value:

  1. If you are calling a function that needs to modify its arguments, use pass-by-reference or pass-by-pointer. Otherwise, you’ll get a copy of the argument.
  2. If you're calling a function that needs to take a large object as a parameter, pass it by const reference to avoid making an unnecessary copy of that object and taking a large efficiency hit.
  3. If you're writing a copy or move constructor which by definition must take a reference, use pass by reference.
  4. If you're writing a function that wants to operate on a polymorphic class, use pass by reference or pass by pointer to avoid slicing.
Hector answered 13/2, 2011 at 19:35 Comment(18)
At case #1, passing by reference is preferred if you don't want a NULL argument so I wouldn't exactly use "interchangeably"Roseannroseanna
Reason 1 - is a violation of the principal of least suprise (when applied to reference instead of pointer. Reason 2 is a violation of encapsulation. Reason 3, is correct and ironic - implement the machinery to avoid pass by reference semantics in the rest of the class. Reason 4 and 5 prefer pass by pointer reference.Biel
@Marlon- thanks for pointing that out; I've updated my answer. To whoever downvoted: can you please explain what I can do to make this answer better?Hector
@Chris Becke- Can you elaborate on your answer? I disagree with almost all your assertions, so I'd like to hear your perspective on this.Hector
pass-by-pointer... I wonder if that term is in the C++ language specification.Oblige
@Chris How does 2 violate encapsulation? If anything else, 1 is a violation of encapsulation (as in instead of someFunc(obj) it would be best to use obj.someFunc()) but there are plenty of times where 1 is still preferred such as void someFunc(Obj obj) { this.registerSomeFunc(); obj.someFunc(); } Regardless, 2 is all but required for all but the most trivial objects.Cardamom
@glow - the largeness of an object is encapsulation leakage. The user of an object should not know, or care, how "large" its data structures are.Biel
I disagree. The internal size of an object is up to it to decide if they want to tell me or not. The physical dimensions of the object is not an internal attribute, but an external one, and should not be considered protected by encapsulation.Cardamom
@Chris Becke- you do have a point about the size of an object. However, my follow-up question is this: given that in C++ all parameters are passed by value by default, which can cost substantially more than a pass-by-reference due to the copy, what alternative do you suggest to pass by reference to avoid unnecessary performance hits? And doesn't the same argument apply to pass by pointer? Also, for your last two points, why is pass by pointer a better choice? Is there a particular reason that it's a more elegant approach than pass by reference? Finally, how does 1 violate least surprise?Hector
As you point out, by default, all parameters are passed by value. Given a function call: int y = f(x); it would be surprising if x was modified by f(). int y = f(&x); on the other hand is gives a very strong hint that x might be modified by the call.Biel
@Chris: I've heard this argument of pointers every now and then, but frankly I am not impressed. You exchange a "notification" of the caller for a possible nullptr issue. I prefer securing against the latter and give the function a sufficiently descriptive name so that it expresses the possible modification... if any.Uncovered
And why is the nullptr issue so bad? Its not like (a) its not easy to trap with modern static analysis, (b) at runtime with assert(), or (c) programmers are not that prone to just passing in zero. All you do is make life inconvenient for people who DO have a pointer to something, and now need to turn it into a reference: *ptr. Which might change the label of the error, but not the error. So pass-by-&-reference gains no safety even, at the expense of readability, and utility.Biel
"If you're writing a function that wants to operate on a polymorphic class, use pass by reference or pass by pointer to avoid slicing." - slicing in terms of memory allocation ? could smdy elaborate ?Bianchi
@antitrust- You might want to do a Google search for "C++ object slicing" for a discussion of this behavior. It's not easily explained in a comment.Hector
Great answer mostly, but #5 is bad: Returning an object, even if uncopyable or very big, by value is the way to go there. If possible, avoid the need for NRVO, but don't agonize over it, any decent compiler does it as a QOI-issue.Homolographic
What is a "large object"? Also, in modern C++, return value copy elision is guaranteed, so "copyability" is not necessary. -1.Foray
@Foray Thanks for pointing that out. I wrote this answer ten years ago when move semantics and copy elision weren’t an option and many functions did use outparameters this way. I’ve updated the answer to remove that part.Hector
@templatetypedef: Un-downvoted you, but still no upvote because of the vague term "large object".Foray
T
9

There are several considerations, including:

Performance

Passing by value copies the data, so passing large data structures by value can inhibit performance. Passing by reference passes only a reference (basically the address) to the data. For large data structures, this can greatly improve performance. For smaller data structures (like an int), passing by reference can inhibit performance.

Modifications

Passing by value copies the data so if the target code modifies that copy, it will not affect the original. Passing by reference passes only the address of the data, so modifications made against that reference will be "visible" to the calling code.

Tardiff answered 13/2, 2011 at 19:33 Comment(2)
Why would passing by reference small data structures as an int inhibit performance?Army
@Kyle_the_hacker: Locality of reference, i imagine.Lactary
C
4

here's the simple rule:

pass by reference when the value is large.

the other answers are amazing. Just trying to make this simplest.

Cirrus answered 22/1, 2022 at 17:2 Comment(0)
T
2

Yes.

Pass by value for things like native types that are small enough that passing them directly is efficient. Otherwise use pass by (const) reference.

The hard part is writing a template that could apply to either (in which case, you usually want to use pass by reference -- the potential penalty for passing a large object by value is much worse than the potential penalty for passing by reference when passing by value would have been preferred).

Edit: this, of course, is assuming a situation where the required semantics would allow either one -- obviously if you're working with something like polymorphic objects, there's no real "preference" involved, because you must use a pointer or reference to get correct behavior.

Tissue answered 13/2, 2011 at 19:34 Comment(0)
S
2

As others already have replied to your question sufficiently well, I would like to add an important point:

If the class does not have public copy-constructor, then you don't have choice to pass by value; you have to pass by reference (or you can pass pointer).

The following program would not compile:

class A
{
public:
     A(){}
private:
     A(const A&) {}
};

//source of error : pass by value
void f(A ) {}

int main() {
        A a;
        f(a);
    return 0;
}

Error:

prog.cpp: In function ‘int main()’:
prog.cpp:10: error: ‘A::A(const A&)’ is private
prog.cpp:18: error: within this context
prog.cpp:18: error: initializing argument 1 of ‘void f(A)’

See yourself at ideone : http://www.ideone.com/b2WLi

But once you make function f pass by reference, then it compiles fine : http://www.ideone.com/i6XXB

Stalinism answered 13/2, 2011 at 19:39 Comment(0)
F
2

You have tagged your question with both C and C++.

Therefore, I suggest that you consider using pass by reference in C++ which supports this feature and that you do not consider using it in C which does not support this feature.

Farant answered 13/2, 2011 at 19:40 Comment(0)
T
0

pass by reference can be called only in below conditions:

Pass-by-references is more efficient than pass-by-value, because it does not copy the arguments. The formal parameter is an alias for the argument. When the called function read or write the formal parameter, it is actually read or write the argument itself.

The difference between pass-by-reference and pass-by-value is that modifications made to arguments passed in by reference in the called function have effect in the calling function, whereas modifications made to arguments passed in by value in the called function can not affect the calling function.

Use pass-by-reference if you want to modify the argument value in the calling function. Otherwise, use pass-by-value to pass arguments.

The difference between pass-by-reference and pass-by-pointer is

that pointers can be NULL or reassigned whereas references cannot. Use pass-by-pointer if NULL is a valid parameter value or if you want to reassign the pointer. Otherwise, use constant or non-constant references to pass arguments.

Thistledown answered 3/1, 2019 at 19:0 Comment(0)
B
-4

While pointers are references, "reference" in c++ usually refers to the practice of tagging a parameter of SomeType&.

Which you should never do. The only place it is appropriate is as a magic syntax required to implement the various pre-defined operators. Otherwise:

  • You should never pass out parameters by reference - pass by pointer, otherwise you make code reviews all but impossible. Pass by reference makes it impossible to tell by examining a call which parameters can be expected to be changed.

  • You should never pass in parameter by reference either. Again, this means you are performing a meta optimization. You should always just pass-by-value, otherwise you are guilty of peeking inside an object, examining its implementation and deciding that pass-by-reference is preferred for some reason.

Any c++ class should implement all the copy and assignment constructors and overloads necessary to be passed around by value. Otherwise it has not done its job, of abstracting the programmer from the implementation details of the class.

Biel answered 13/2, 2011 at 19:55 Comment(6)
1) Even if you do pass by value, you can't tell that by looking at the function call. 2) How about this then. Always pass by reference, unless you need a copy, otherwise you are guilty of peeking inside an object, examining it's implementation and deciding that pass-by-value is preferred for some reason.Guardroom
Because pass-by-value is the default semantic of any programming language that is written in terms of function calls.Biel
Who cares if it's the default? It's slow. If you were writing a bit blit function, would you seriously pass the bitmap in by value? Or if you wrote a function to tell you if a database contained a certain entry, you'd pass the whole database in by value?Guardroom
If i had a Bitmap class, It would contain a pointer to the bitmap bits. Passing the Bitmap class would invoke a O(1) cost of copying the pointer.Biel
So, your copy constructor wouldn't actually copy?Guardroom
Use a const reference if you want to guarantee the argument will not be modified...Keyes

© 2022 - 2024 — McMap. All rights reserved.