6

I must have a fundamental misunderstanding about C++11. My professors told me it wasn't possible to pass a non-primitive type to a function except by reference or pointer. However, the following code works just fine

#include <iostream>
using namespace std;

class MyClass
{
public: 
    int field1;
};

void print_string(string s) { 
    cout << s << endl; 
}

void print_myclass(MyClass c) { 
    cout << c.field1 << endl; 
}

int main(int argc, char *argv[]) 
{
    string mystr("this is my string"); 
    print_string(mystr); // works
    MyClass m; 
    m.field1=9; 
    print_myclass(m); 
    return 0;
}

Running the program yields the following output

this is my string
9

RUN SUCCESSFUL (total time: 67ms)

I'm using MinGW/g++ on Win7

Why does that work? I thought non-primitive types couldn't be passed by value?!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
JLindsey
  • 700
  • 2
  • 7
  • 14
  • 8
    Your professor is wrong, if that's what he said. More likely, he said it isn't advisable. – user207421 Aug 21 '13 at 00:40
  • 3
    @EJP If he said it isn't advisable, as a blanket statement, then he's also wrong ;-) Though it's the "outdated or not thought-out performance advise" kind of wrong, not the "doesn't know language basics" kind of wrong, which is slightly better. –  Aug 21 '13 at 00:41
  • Okay. Thanks! I suppose it isn't advisable because it results in another copy of each parameter object? – JLindsey Aug 21 '13 at 00:42
  • @JaredLindsey, yep it calls the copy constructor on the class unless it has been explicitly deleted or made private. Then your code doesn't compile. If the copy constructor has been overridden, then the arguments passed must fit the definition of a copy constructor you have explicitly defined. – Jonathan Henson Aug 21 '13 at 00:45
  • 1
    Programming professors are not necessarily good programmers. Be warned! – Neil Kirk Aug 21 '13 at 02:15

3 Answers3

10

Non-primitive types can certainly be passed by value. (This is covered in section 5.2.2 [expr.call] of the C++ Standard.)

However, there are a few reasons why this is often discouraged, especially in C++03 code.

First, for large objects, it is less efficient to do so (when compared with passing by reference), as the data is passed on the stack. A reference will take one word on the stack, so passing any object via the stack which is larger than one word will necessarily be slower.

Second, passing by value invokes the copy constructor (or, as @templatetypedef points out, potentially the move constructor in C++11). This additional processing could incur a certain amount of overhead.

Third, you may have intended to modify the passed in object, but by passing in a copy (by value), any changes you make within the function will not affect the original object. So it is important to get the semantics correct (ie. whether or not you want to modify the original). Hence this is a potential bug in some circumstances.

Finally, if there is a poorly written class with no copy constructor or assignment operator, the compiler will automatically generate a default one for you. This will perform a shallow copy, which could cause problems such as memory leaks. This is yet another good reason why it is very important to implement these special methods. Full details are in this article:

In general for C++03 code, you would normally pass by a const& reference if you don't intend to modify the object, or by normal & reference if you need to modify the object. Use a pointer if the parameter is optional.

Some good answers and discussion are also found in these questions, especially the discussion on move semantics:

A complete answer for C++11 is more complicated:

Probably the best summary of which approach to use:

Community
  • 1
  • 1
gavinb
  • 19,278
  • 3
  • 45
  • 60
  • "usually very inefficient"? Leaving aside the large body of classes which are trivial to copy and only one or a few words large, there's also this: http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/ –  Aug 21 '13 at 00:43
  • 1
    If copying leads to leaks/errors because the default is wrong, then it's the class's fault and should be fixed there, either by prohibiting copying (pretty easy even pre-C++11 if you don't mind useless error messages) or abiding the rule of three/five/zero. –  Aug 21 '13 at 00:49
  • Hence the "usually" qualifier. It is hard to convey all these subtleties in 25 words or less. Of course, the complete answer is more nuanced. But for teaching newbies, it's a pretty good rule of thumb to start with. – gavinb Aug 21 '13 at 00:50
  • RE performance: How about not giving newbies inaccurate information about issues they don't have to know about yet, and should learn about fully later on anyway? ;-) Apart from this philosophical objection, "usually" and "vast majority" is just not true: There are many, many objects for which it is not *ever* true, and for almost all others, it is only true in limited circumstances, so a "sometimes" or "in specific cases" would be much more appropriate if you are determined to include any commentary on the topic. –  Aug 21 '13 at 00:55
  • Indeed - updated the answer to clarify that leaks are due to poorly written class and copy ctor and assignment op are needed. – gavinb Aug 21 '13 at 00:55
  • Still no need to have memory leaks due to a shallow copy, either. Just use existing RAII classes instead of pointers. – chris Aug 21 '13 at 01:00
3

Your professor is just flat out wrong, maybe he was thinking about JAVA or C#? Everything is passed by value in C++. To pass something by reference you need to pass it with the & modifier.

Jonathan Henson
  • 8,076
  • 3
  • 28
  • 52
  • Everything in Java is pass by value. It's just that reference types are basically pointers, so it seems like you are passing by reference. – user904963 Aug 15 '23 at 21:55
1

Non-primitive types can indeed be passed by value in C++. If you try to do this, C++ will use a special function called the copy constructor (or in some cases in C++11, the move constructor) to initialize the parameter as a copy of the argument. Writing copy constructors and assignment operators is known to be a tricky part of C++ (getting it wrong is easy and getting it right is hard), so it may be the case that the professors were trying to discourage you from doing so. Failing to write a copy constructor or doing so incorrectly can easily lead to program crashes and is a common source of confusion for new C++ programmers.

I'd suggest doing a Google search for "C++ Rule of 3" or "copy constructor assignment operator" to learn more about how to write functions that copy objects intelligently. It takes a bit to get up to speed with how to do this, but once you understand the concepts it's not too hard.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065