views:

1129

answers:

4

I am new to C++ programming, but I have experience in Java. I need guidance on how to pass objects to functions in C++.

Do I need to pass pointers, references, or non-pointer and non-reference values? I remember in Java there are no such issues since we pass just the variable that holds reference to the objects.

It would be great if you could also explain where to use each of those options.

+24  A: 

My rules of thumb:

Pass arguments per const reference, except when

  1. they are to be changed inside the function and such changes should be reflected outside, in which case you pass per non-const reference
  2. the function should be callable without any argument, in which case you pass per pointer, so that users can pass NULL/0 instead; apply the previous rule to determine whether you should pass as a pointer to a const argument
  3. they are of built-in types, which can be passed per copy

There's more to this, but these few beginner's rules will get you quite far.

sbi
+1 - I would also note that some (i.e. Google) feel that objects that will be changed within the function should be passed via a pointer instead of a non-const reference. The reasoning being that when the address of an object is passed to a function it is more apparent that said function may change it. Example: With references, the call is foo(bar); whether the reference is const or not, with a pointer it is foo( and is more apparent that foo is being passed a mutable object.
RC
@RC Still doesn't tell you if the pointer is const or not. Google's guidelines have come in for a lot of flak in the various C++ online communities - justifiably so, IMHO.
anon
While in other contexts google may be leading the way, in C++ their style guide is not really that good.
David Rodríguez - dribeas
@David Rodriguez - dribeas: Can you suggest some better C++ style guide? Preferably with a style checker?
ArunSaha
David Rodríguez - dribeas
A: 

Unlike in C, you can pass objects in C++ by value. In this case, a copying constructor of given object is called ( either default or user-created ) and a new instance of given object is created as a variable local to the function called.

Of course this causes all the negative conseqences of doing so, like big memory and CPU overhead, caveats of copying objects that can't be copied simply, and so on, so in day-to-day use, passing objects by pointers or references is usually much more desirable.

SF.
In C, all objects are passed by value, except for arrays.
anon
In C, arrays can't be passed at all? The function receives a pointer to the first item which - again - is passed by value.
visitor
Not quite correct - an array _degrades_ to a pointer to the first element in C (and C++).
Timo Geusch
Still not correct. Arrays decay to pointers.
FredOverflow
It is my understanding that when you do `X arr[n]; foo(arr);` you are technically passing a pointer (and it is semantic hairsplitting, whether you say the array is passed by reference - which is something you can *actually* do in C++ -, or whether you say the array can't be passed at all). - Come to think of it, array is used both in a strict and a non-strict sense, so one could also argue that arrays strictly speaking aren't even indexable (arr[n] <=> *(arr + n) where the type of arr clearly isn't an array) ;)
UncleBens
+13  A: 

There are some differences in calling conventions in C++ and Java. In C++ there are technically speaking only two conventions: pass-by-value and pass-by-reference, with some literature including a third pass-by-pointer convention (that is actually pass-by-value of a pointer type). On top of that, you can add const-ness to the type of the argument, enhancing the semantics.

Pass by reference

Passing by reference means that the function will conceptually receive your object instance and not a copy of it. The reference is conceptually an alias to the object that was used in the calling context, and cannot be null. All operations performed inside the function apply to the object outside the function. This convention is not available in Java or C.

Pass by value (and pass-by-pointer)

The compiler will generate a copy of the object in the calling context and use that copy inside the function. All operations performed inside the function are done to the copy, not the external element. This is the convention for primitive types in Java.

An special version of it is passing a pointer (address-of the object) into a function. The function receives the pointer, and any and all operations applied to the pointer itself are applied to the copy (pointer), on the other hand, operations applied to the dereferenced pointer will apply to the object instance at that memory location, so the function can have side effects. The effect of using pass-by-value of a pointer to the object will allow the internal function to modify external values, as with pass-by-reference and will also allow for optional values (pass a null pointer).

This is the convention used in C when a function needs to modify an external variable, and the convention used in Java with reference types: the reference is copied, but the referred object is the same: changes to the reference/pointer are not visible outside the function, but changes to the pointed memory are.

Adding const to the equation

In C++ you can assign constant-ness to objects when defining variables, pointers and references at different levels. You can declare a variable to be constant, you can declare a reference to a constant instance, and you can define all pointers to constant objects, constant pointers to mutable objects and constant pointers to constant elements. Conversely in Java you can only define one level of constant-ness (final keyword): that of the variable (instance for primitive types, reference for reference types), but you cannot define a reference to an immutable element (unless the class itself is immutable).

This is extensively used in C++ calling conventions. When the objects are small you can pass the object by value. The compiler will generate a copy, but that copy is not an expensive operation. For any other type, if the function will not change the object, you can pass a reference to a constant instance (usually called constant reference) of the type. This will not copy the object, but pass it into the function. But at the same time the compiler will guarantee that the object is not changed inside the function.

Rules of thumb

This are some basic rules to follow:

  • Prefer pass-by-value for primitive types
  • Prefer pass-by-reference with references to constant for other types
  • If the function needs to modify the argument use pass-by-reference
  • If the argument is optional, use pass-by-pointer (to constant if the optional value should not be modified)

There are other small deviations from these rules, the first of which is handling ownership of an object. When an object is dynamically allocated with new, it must be deallocated with delete (or the [] versions thereof). The object or function that is responsible for the destruction of the object is considered the owner of the resource. When a dynamically allocated object is created in a piece of code, but the ownership is transfered to a different element it is usually done with pass-by-pointer semantics, or if possible with smart pointers.

Side note

It is important to insist in the importance of the difference between C++ and Java references. In C++ references are conceptually the instance of the object, not an accessor to it. The simplest example is implementing a swap function:

// C++
class Type; // defined somewhere before, with the appropriate operations
void swap( Type & a, Type & b ) {
   Type tmp = a;
   a = b;
   b = tmp;
}
int main() {
   Type a, b;
   Type old_a = a, old_b = b;
   swap( a, b );
   assert( a == old_b );
   assert( b == old_a ); 
}

The swap function above changes both its arguments through the use of references. The closest code in Java:

public class C {
   // ...
   public static void swap( C a, C b ) {
      C tmp = a;
      a = b;
      b = tmp;
   }
   public static void main( String args[] ) {
      C a = new C();
      C b = new C();
      C old_a = a;
      C old_b = b;
      swap( a, b ); 
      // a and b remain unchanged a==old_a, and b==old_b
   }
}

The Java version of the code will modify the copies of the references internally, but will not modify the actual objects externally. Java references are C pointers without pointer arithmetic that get passed by value into functions.

David Rodríguez - dribeas
@david-rodriguez-dribeas I like the rules of thumb section, specially "Prefer pass-by-value for primitive types"
yadab
+1  A: 

There are several cases to consider.

Parameter modified ("out" and "in/out" parameters)

void modifies(T &param);
// vs
void modifies(T *param);

This case is mostly about style: do you want the code to look like call(obj) or call(&obj)? However, there are two points where the difference matters: the optional case, below, and you want to use a reference when overloading operators.

...and optional

void modifies(T *param=0);  // default value optional, too
// vs
void modifies();
void modifies(T &param);

Parameter not modified

void uses(T const &param);
// vs
void uses(T param);

This is the interesting case. The rule of thumb is "cheap to copy" types are passed by value — these are generally small types (but not always) — while others are passed by const ref. However, if you need to make a copy within your function regardless, you should pass by value. (Yes, this exposes a bit of implementation detail. C'est le C++.)

...and optional

void uses(T const *param=0);  // default value optional, too
// vs
void uses();
void uses(T const &param);  // or optional(T param)

There's the least difference here between all situations, so choose whichever makes your life easiest.

Const by value is an implementation detail

void f(T);
void f(T const);

These declarations are actually the exact same function! When passing by value, const is purely an implementation detail. Try it out:

void f(int);
void f(int const) { /* implements above function, not an overload */ }

typedef void NC(int);       // typedefing function types
typedef void C(int const);

NC *nc = &f;  // nc is a function pointer
C *c = nc;    // C and NC are identical types
Roger Pate