4

In Java, all variables containing proper objects are actually references (i.e. pointers). Therefore, method calls with these objects as arguments are always "by reference". Calling a method which modifies the state of the object also affects the original object (on the caller side).

C++ is different: Here arguments can be passed by value or passed by reference. Calling a mutator method on an object which was passed by value leaves the original object unaffected. (I suppose call by value creates a local copy of the object).

So my first response to this - coming from Java to C++ - is: ALWAYS use pointers when using objects as arguments. This gives me the behavior I have come to expect from Java.

However, one could also use "call by value" in case one does not need to modify the object in the method body. Are there reasons why one would want to do this?

clstaudt
  • 21,436
  • 45
  • 156
  • 239
  • If I'm not mistaken it is not a straightforward thing to pass a reference type object by value in any environment. Usually any reference type is passed by ref. What if your object is a singleton? It is impossible to send it by value by design. – Oybek Apr 22 '12 at 15:44
  • 2
    Not many. Calling a massive, lengthy, complex, contention-multiplying copy ctor just because it's less typing is part of the fun of C++. :(( – Martin James Apr 22 '12 at 15:46
  • 3
    Note that C++ and Java disagree on what a reference is (a Java reference is basically a C++ pointer without pointer arithmetic) - and what you call "pass by reference" is just [pass-by-value](http://stackoverflow.com/q/40480/395760), with the values being object references. C++ on the other hand has proper pass-by-reference, which allows fun like `void swap(T &a, T &b)` which isn't possible in Java. –  Apr 22 '12 at 15:50
  • 1
    @Oybek: In C++, there's no such thing as a "reference type object"; there are objects, and there are references (and pointers) to objects. But you're correct that objects can only be passed by value if they're copyable. – Mike Seymour Apr 22 '12 at 15:56

8 Answers8

13

ALWAYS use pointers when using objects as arguments

No, in C++ always pass by reference, unless your function can be called with nullptr as a valid argument. If the function does not need to modify the argument, pass by const reference.

Passing arguments by value has several uses.

If your function needs to create a copy of the argument it is better to create this copy by passing by value rather than creating a copy within the function. For instance:

void foo( widget const& w )
{
  widget temp( w );
  // do something with temp
}

Instead use

void foo( widget w )  // copy is made here
{
  // operate on w itself
}

Doing this also has the benefit of allowing the compiler to move widget if possible, which is generally more efficient than creating copies.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • I would like to know more about "move" semantics Praetorian, especially in the context of something like this. From what I read, the advantage is "the operation not only forgoes the expense of a deep copy, but is safe and invisible.". To which objects does this apply? It's container specific, presumably? – Robinson Apr 22 '12 at 16:05
  • 2
    @Robinson [Here's](http://stackoverflow.com/a/3109981/241631) an excellent answer describing move semantics – Praetorian Apr 22 '12 at 16:09
9

You're wrong in that you should pass by pointer. If you want to pass by reference, well... simply pass by reference:

void foo(int& x)
{
   x = 3;
}

int main()
{
   int a = 0;
   foo(a);
   assert( a == 3 );
}

Also, note that passing by value guarantees that the your variable can't be changed inside the called context. Although so would passing by const reference...

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 1
    This is _terrible_ practice IMO. If you plan to modify a function argument, pass it by address: `foo(int *x)`. This explicitly tells the users of your API that the value will be modified. The _only_ (for some value of "only") reason to pass by (non-const) reference is when you're overloading an operator. – Adam Liss Apr 22 '12 at 15:44
  • @AdamLiss first off, there's no such thing as pass by address. Second off, passing by reference is prefered to passing by pointer. Unless you can back up your opinion with sound arguments... (doubt it) – Luchian Grigore Apr 22 '12 at 15:45
  • Why is passing by reference prefered to passing by pointer? What are disadvantages of passing by pointer? – clstaudt Apr 22 '12 at 15:46
  • 1
    http://stackoverflow.com/questions/334856/are-there-benefits-of-passing-by-pointer-over-passing-by-reference-in-c – Adam Liss Apr 22 '12 at 15:48
  • 2
    That also works the other way: references cannot be null. That situation is the only reason I'd have for choosing a pointer over a reference. – chris Apr 22 '12 at 15:51
  • 1
    @AdamLiss You're muddling the issue. Yes, make `foo` take an `int *` argument **if** `foo(nullptr)` is a valid call, otherwise you should always make `foo` take an `int&`. – Praetorian Apr 22 '12 at 15:52
  • No, the advantage is in maintainability: taking a non-const reference and then modifying the value is a great way to generate bugs after the original developer has left. – Adam Liss Apr 22 '12 at 15:52
  • 1
    I don't agree at all here Adam. Where possible I will pass by const reference if the intention is that the parameter shall not be modified, and by reference if it is. Otherwise I will pass a std::shared_ptr if the object is dynamically allocated. I NEVER pass a raw pointer unless I have no choice because I'm dealing with a third party interface. – Robinson Apr 22 '12 at 15:53
  • 2
    @AdamLiss I beg to differ. The signature clearly says "this value can be modified" as much as the pointer equivalent does. – juanchopanza Apr 22 '12 at 15:54
  • 1
    I'll agree if it takes an `int const&`. Otherwise you're asking for trouble because a call to a function that modifies its arguments looks _exactly_ like a call to a function that does not. It's always better to make your intentions explicit, especially when there's no associated loss in performance. See http://stackoverflow.com/questions/410593/pass-by-reference-value-in-c – Adam Liss Apr 22 '12 at 15:55
  • 4
    @Adam: Using address-of to express mutability is an artifact of C design APIs. The C++ way is to use reference and const reference. I am actively asking my peers in every code review I see they use `*` to use `&` unless is an optional argument that can accept a `NULL` – Remus Rusanu Apr 22 '12 at 15:56
  • 3
    @AdamLiss No, you're not asking for any trouble, unless you're working with a developer such as yourself who draws such a great distinction between a mutable reference and a pointer. `foo(int&)` clearly indicates that `foo` may modify the argument in the caller's context. The only thing `foo(int*)` tells me is that I can call `foo` with `nullptr` as the argument, and the function *should* handle that. However, if the function doesn't check for that you've got a buggy program on your hands. – Praetorian Apr 22 '12 at 15:58
  • Can you return a reference by reference, eg. in a factory method, like with pointers: 'getObject(myClass **outOb)'? – Martin James Apr 22 '12 at 16:08
  • 1
    @MartinJames I think a proper factory returns an object, not constructs it in a parameter - either reference or pointer. – Luchian Grigore Apr 22 '12 at 16:12
  • @MartinJames: In principle you can, but its a bit weird. You'd usually return something as a return value, and a factory should return a smart pointer. (In C, it's quite common to do it like that, allowing you to use the return value for an error code; but we're talking about C++, where you'd report failure with an exception.) – Mike Seymour Apr 22 '12 at 16:15
  • 1
    I agree with Adam, even though the prototype shows that the referent can be modified, there is nothing *at the call site* which indicates that. So someone looking at the code, without checking the prototype of each called function, doesn't know a modification can happen. – Keith Randall Apr 22 '12 at 16:16
  • 2
    @KeithRandall how would you call a function without knowing its prototype? – Luchian Grigore Apr 22 '12 at 16:22
  • I often have pointer vars that point to different objects as the app runs. This seem to be impossible with references - they have to be initialized where they are defined and cannot be reloaded later. – Martin James Apr 22 '12 at 16:26
  • @MartinJames I don't get it. How would you "reload" a pointer? You can initialize a reference once, as you do with a pointer... – Luchian Grigore Apr 22 '12 at 16:30
  • 1
    @LuchianGrigore - you can't, but that's not the issue Try maintenance and enhancement on someone else's complex code that is riddled with references. It's the same in Delphi/Pascal where a reefrence is generated by the 'var' keyword and, when browsing the code, you have no idea that a side-effect is possible when changing a parameter value. Luckily, Delphi has pointers as well, so no need to use 'var', (and I don't, ever). – Martin James Apr 22 '12 at 16:34
  • @Luchian: I wouldn't. See Martin comment. – Keith Randall Apr 22 '12 at 16:37
  • 1
    @LuchianGrigore - easy, just change its value to something else, NULL maybe, or some value popped from a queue or list. No need to initialize it. – Martin James Apr 22 '12 at 16:37
  • 1
    @MartinJames "complex code that is riddled with references" I don't find references riddling, and since most code is written like that, I suggest you get accustomed to them, instead of blindly thinking you're doing yourself any favors by using pointers instead. – Luchian Grigore Apr 22 '12 at 16:39
  • I'm afraid that I look upon these reference types as a form of obfuscation. Address operators like & and @ are just fine - I know immediately what is going on without continually referring to header files. I'm not only doing myself favours by using pointers, I'm also helping the guy who comes next. I've had decades of this 'var' stuff in Pascal/Delphi, now it's crept into C++ as well. Anyone who thinks it's a good idea should take a good dose of maintenance and debugging on legacy code - it's a very effective cure. – Martin James Apr 22 '12 at 16:46
  • 1
    @MartinJames Don't fool yourself into thinking you're doing anyone any favors. It's not obfuscating code. It's very clear for a C++ programmer what a reference is. – Luchian Grigore Apr 22 '12 at 16:54
  • @LuchianGrigore We should just agree to differ on this one. – Martin James Apr 22 '12 at 17:06
  • 2
    @MartinJames we can do that, or you could actually take something from this conversation and instead of insisting you're right, just go and learn a **basic** C++ concept, so that it doesn't appear so weird and obfuscating for you. It's just a friendly piece of advice. – Luchian Grigore Apr 22 '12 at 17:08
  • @MartinJames You are free to look upon reference types as a 'form of obfuscation', but that's not a law of the universe, it's just one highly personal opinion. They are a feature of the language like all the others. My own view is that programmers should understand the language they are using. All of it. – user207421 Apr 22 '12 at 23:51
  • I find the true solution of the problem is to use `const` wherever possible and factor long functions into smaller functions. Then each sub-function states whether its value is `&` or `const &`. I find this to be a much better solution than using pointers (which I pretty much only use for `nullptr`). – David Stone Apr 30 '12 at 00:18
3

If you pass objects to a function by value, that function is free to use those objects as "working" variables without affecting the caller.

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • Doesn't really compensate for the copy ctor overhead, unless class is trivial. – Martin James Apr 22 '12 at 15:48
  • 1
    @MartinJames the alternative would be to pass by const reference and make a temporary copy anyway. Plus, C++11 does away with a lot of unnecessary copying. – juanchopanza Apr 22 '12 at 15:51
  • What is the point of modifying members in some copy? The modified data is not going anywhere and will soon be destroyed anyway. I don't really get the 'working variables' argument. – Martin James Apr 22 '12 at 16:06
3

in Java, a reference is a garbage-collected "smart pointer".

C++ also uses the concept of smart pointers, which are in the <memory> library, called unique_ptr and shared_ptr. shared_ptr is reference-counted so can be used in the same way as Java References. unique_ptr is similar, except is noncopyable and a little more lightweight. The benefit of both is never ever needing to use the delete keyword, and being able to rely on "pointers" which are protected by exceptions.

C++ also supports the concept of a reference - which is usually a good choice for passing objects around (And even better is reference-to-const). References in C++ are bound to the type of object which is passed, so you need to specify (using the reference symbol &) in the function signature

#include <string>

void foo(std::string& bar)
{
    bar = "world";
}

void foo2(const std::string& bar)
{
    //passed by reference, but not modifyable.
}

int main()
{
    std::string str = "hello";
    foo(str);
    foo2(str);
}

As for "raw" pointers - you can nearly always avoid them by using either a smart pointer, a reference, an iterator, or pass-by-value. plain ordinary pointers come with a mixed bag of "gotchas" which C++ inherited from the C language - if you've got a fairly recent compiler you should never really need to use them at all (unless you're going to be doing things like re-inventing the wheel for learning purposes with memory management, data structures, etc.)

Ben Cottrell
  • 5,741
  • 1
  • 27
  • 34
  • What mixed bag? Mine has only one item in it - you must manage object lifetime yourself. Smart pointers have their own issues, certainly with multithreaded code that passes pointers to system calls, eg. for later callback or for inter-thread comms. Having such an object destroyed because it's gone out of scope in the calling function will generate an AV/segfault, often somewhere that seems unrelated to the point where the error was generated. 'Smart' pointers are OK for single-threaded 'hourglass' apps. Anything complex and you quickly realize that 'smart' can mean 'obfuscated lifetime'. – Martin James Apr 22 '12 at 22:28
  • A Java reference is neither garbage collected nor smart. It is most akin to a Pascal pointer. The *objects so referenced* are subject to GC. – user207421 Apr 22 '12 at 23:55
3

You normally pass by value because something is a value and should act like a value. In many cases passing by const reference is close enough to the same to be worth considering. In other cases, it's not.

Passing by value can also be an optimization. At least IMO, this more or less secondary, but it can be important anyway (especially in choosing between passing by const reference and passing a real value.

IMO, the real question should be in the opposite direction: why should the compiler pass a reference when you've clearly told it to pass a value? The answer is "premature optimization". The designers of Java (to mention your example, though it's hardly unique in this) decided that they knew better than to let the compiler do what it was told. Since passing a large object by value can be slow and might be a mistake, they decided to not let it happen at all, even though it can be fast and may well be exactly what was intended.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

I think you should consider the variable types in your function signature as the contract to the caller. So if you declare you function as:

void foo( int a );

then you are saying i will copy your passed in value do whatever I like and it won't be modified.

If you declare like:

void foo (int* a);

then I may modify what a points to or indeed modify the pointer

so the semantic difference is that you are declaring what the contract of your function may do, with pointers and references (without const declared on the reference or the object that the pointer is pointing to) you may modify the variable.

References are preferred in C++ as it is clearer what you are intending and you avoid the c style pointer to pointer function signatures that are necessary when passing pointers to functions where you want to modify what the pointer is pointing to, which leads to errors and head scratching before you realise what went wrong.

Pointers though still are very useful as paremeters, especially if you need to test if the pointer to the object is null or not, something that is not possible to do using references.

EdChum
  • 376,765
  • 198
  • 813
  • 562
  • References are not preferred by everyone! – Martin James Apr 22 '12 at 17:25
  • @MartinJames fair point, will amend my advice, from my perspective we pass smart pointers everywhere and always need to test whether the pointer is null or not, something you cannot do using a reference. – EdChum Apr 22 '12 at 19:02
  • Even smart pointers can have issues. An object pointer posted off on a system call like Windows.PostMessage(), (something I do often), must not be destroyed just because it's gone out of scope in the calling method. This issue can lead to all sorts of shenanigans with refCounted types like smartPointers and interface types. 'Dumb' pointers are, of course, just fine. – Martin James Apr 22 '12 at 22:18
  • @MartinJames another good point but I would not want to pass a ptr to an object where it could get destroyed in the manner you mention, typically your object would be a ptr copy of a resource whose lifetime is not tied to the lifetime of the message to another window unless that is what you wanted but I take your point it is another thing to consider for the correct type of ptr to use. – EdChum Apr 22 '12 at 22:23
1

When passing by reference there is an inherit danger that you could inadvertently change the value passed to the method, inside the method. After the method call you could assume the method didn't change the object, when in fact it did.

Passing by value has the negative aspect of extra memory required (and possibly a slight performance overhead) because you make a copy of the object you are passing in, but with the benefit that you can be sure your object passed into the method will not be modified inside the method.

user997112
  • 29,025
  • 43
  • 182
  • 361
  • 'you could assume the method didn't change the object, when in fact it did' - this is jsut a mindset issue. If you have access to an object and change some member data ins ome way, then you are changing member data on the object! Changing member data on a copy usually means you are wasting your time since your changed values are about to be destroyed. If developers would make the much more reasonable assumption that they only ever get the one object instance and there are no copies, they should be fine. – Martin James Apr 22 '12 at 17:12
0

It's helpfull to avoid side-effects. If you program need such side-effect use call by reference.

Aslan986
  • 9,984
  • 11
  • 44
  • 75
  • 1
    you can avoid side-effects by passing by const reference. – juanchopanza Apr 22 '12 at 15:44
  • Yes yes, sure, and it's more efficient (also if modern compiler often turn automatically to pass by const reference). I was just explaining the main difference with passing by value. – Aslan986 Apr 22 '12 at 15:48
  • What I am saying is that the side-effects argument isn't the important one, because you can get it with const reference and avoid a copy. – juanchopanza Apr 22 '12 at 16:00