4

So I am a fairly experienced C programmer who has to program in C++ a lot. There are some subtleties about the language that I have never felt to confident on. For example, the best methods of passing arguments.

Suppose for example, that I have a black-box class called Object (it might have a lot of member variables for all we know), and a function fn that takes a vector of Object instances as its argument. It seems to me that there are four basic ways of passing this:

void fn(vector<Object> vec);
void fn(vector<Object*> vec);
void fn(vector<Object> *vec);
void fn(vector<Object> &vec);

And of course, we could also take some combination of these features.

I want to make sure I have this straight:

Method 1 would copy the vector class including a copy of each Object instance in the vector. This could potentially be a huge overload and thus is bad.

(this one I'm not so sure on) Method 2 would copy all of the method variables of vec, but it would only copy the addresses of each of the Object instances. I don't know enough about what's contained in the vector class to know whether this is advisable or not.

Method 3 & 4 are fairly straightforward and similar to one another and introduce minimal overhead.

Is all this correct? and which is the preferred method keeping in mind we know nothing about the Object class?

sicklybeans
  • 299
  • 3
  • 11
  • 3
    The preferred method depends on what you want to do, but if you do not want to modify the vector, and you do not want to copy, then passing a `const` reference is the usual approach: `void fn(const vecctor& vec);`. – juanchopanza Aug 06 '13 at 17:28
  • possible duplicate of [How to pass parameters correctly?](http://stackoverflow.com/questions/15600499/how-to-pass-parameters-correctly) - except that your second option isn't covered there beacuse it's not completely related – stijn Aug 06 '13 at 17:40

4 Answers4

3

The preferred way to pass an argument would be
void fn(const vector<Object> &vec);
Provided of course you don't modify it. Using pointers, like in case 2 and 3 is generally not good practice in c++.
Now version 1:
void fn(vector<Object> vec);
Would generally be considered bad because it makes a copy but sometimes it is necessary. Keep in mind that sometimes the compiler is able to optimize the copy away using copy elision in the case of temporaries and if the copy isn't modified(but you should be using a const ref in that case)

aaronman
  • 18,343
  • 7
  • 63
  • 78
  • But version 3 could be preferred if you need a copy inside the function, since it allows some scope for copy elision. – juanchopanza Aug 06 '13 at 17:29
  • @juanchopanza isn't that what I'm saying, I ended it with necessary – aaronman Aug 06 '13 at 17:30
  • Almost. It doesn't always make a copy. – juanchopanza Aug 06 '13 at 17:30
  • @juanchopanza if you modify it, won't it always make a copy? – aaronman Aug 06 '13 at 17:31
  • I mean, if you pass a temporary value, the copy can be elided. – juanchopanza Aug 06 '13 at 17:32
  • @juanchopanza shouldn't that be handled by moving in c++11 – aaronman Aug 06 '13 at 17:32
  • It would be handled by copy elision both pre-c++11 and in C++11. *If* copy elision does not take place, *then* it could be moved (in C++11, obviously). – juanchopanza Aug 06 '13 at 17:34
  • @juanchopanza well I'm not too familiar with CE but I mentioned it now – aaronman Aug 06 '13 at 17:36
  • http://stackoverflow.com/questions/7592630/is-pass-by-value-a-reasonable-default-in-c11 – stijn Aug 06 '13 at 17:42
  • @stijn this basically supports what I'm saying const ref is the best, at this point I feel like there are so many ways to pass variables in c++ you should really just decide the conventions you want to follow in your code base – aaronman Aug 06 '13 at 17:44
  • you should read again. const ref is not always 'the best'. Only when you're only planning to inspect the argument. If you're going to modify it, pass by reference. But you shouldn't _generally_ consider pass-by-avlue as bad. Instead, if you're going to copy it anyway, pass the argument by value (and eventually move from it). For example see here: http://stackoverflow.com/questions/7592630/is-pass-by-value-a-reasonable-default-in-c11 – stijn Aug 07 '13 at 07:23
  • @stijn when I said always the best I really meant, when you don't modify you should make sure to do it, as for the rest of your argument it seems like your mostly agreeing with me, i neglected to talk about moving because the question wasn't tagged c++11 – aaronman Aug 07 '13 at 18:23
1

This is all correct. Method 2 would indeed make a copy of vec, and it since its elements are pointers to Object it would only copy those pointers, and not the actual objects. This is not very different from passing in an array of pointers to Object.

Generally, having vectors of pointers is frowned upon. When a vector goes out of scope, those pointers will not be deleted automatically. You would have to be sure to delete them yourself. And if your will still get a memory leak if your code throws an exception at any point before that.

If the objects are small, you should use a vector of objects, not pointers. If the objects are large, you should use a vector of smart pointers.

Dima
  • 38,860
  • 14
  • 75
  • 115
1

Everything you say is correct.

If you are going to be copying the vector anyway inside the function, or if you want to modify vec inside fn, but not modify the original copy, then use #1. Then you can save yourself the copy inside and just let the compiler (possibly) do the copy for you.

If you want to modify the original vector (from outside fn), then use #4.

If you do not want to modify the vector at all, then use #4 with a const:

void fn(const vector<Object> &vec);

Do not use #2 because you will have a very hard time putting a pointer into a vector. The memory will need to be newd and deleted and it will be error prone. [Obligatory shared_ptr note].

Your #3 is not idiomatic C++. It's fine, but idiomatic C++ eschews pointers in favor of references in this case.

JoshG79
  • 1,685
  • 11
  • 14
  • If the function is going to store a handle to the vector somewhere where it will be used later (after the function returns), use #3. Idiomatic C++ uses pointers (or smart pointers) when there are lifetime issues to deal with. – Ben Voigt Aug 06 '13 at 17:37
0

Basically you got it right and the others have gave nice answer and remarks. I just want to add two comments:

1) If you have

std::vector<Object> v;

then, obviously, you can't call fn(v) for the overload void fn(vector<Object*> vec);.

2) You said:

"I don't know enough about what's contained in the vector class" ...

The most popular implementations of std::vector have only three pointers as data members and the cost of copying these three pointers is very small. However, this is not what happens when you copy a std::vector (it would be if its copy constructor was compiler generated). Actually std::vector has a copy constructor which (as you said) copies all the elements of the original std::vector. This also involves memory allocation and can be a very costly operation.

Cassio Neri
  • 19,583
  • 7
  • 46
  • 68