views:

141

answers:

6

Hi,

How to translate properly the following Java code to C++?

Vector v;
v = getLargeVector();
...
Vector getLargeVector() {
    Vector v2 = new Vector();
    // fill v2
    return v2;
}

So here v is a reference. The function creates a new Vector object and returns a reference to it. Nice and clean.

However, let's see the following C++ mirror-translation:

vector<int> v;
v = getLargeVector();
...
vector<int> getLargeVector() {
    vector<int> v2;
    // fill v2
    return v2;
}

Now v is a vector object, and if I understand correctly, v = getLargeVector() will copy all the elements from the vector returned by the function to v, which can be expensive. Furthermore, v2 is created on the stack and returning it will result in another copy (but as I know modern compilers can optimize it out).

Currently this is what I do:

vector<int> v;
getLargeVector(v);
...
void getLargeVector(vector<int>& vec) {
    // fill vec
}

But I don't find it an elegant solution.

So my question is: what is the best practice to do it (by avoiding unnecessary copy operations)? If possible, I'd like to avoid normal pointers. I've never used smart pointers so far, I don't know if they could help here.

+2  A: 

void getLargeVector(vector& vec) { // fill vec }

Is a better approach for now. With c++0x , the problem with the first approach would go by making use of move operations instead copy operations.

Jagannath
+1  A: 

Why would you like to avoid normal pointers? Is it because you don't want to worry about memory management, or is it because you are not familiar with pointer syntax?

If you don't want to worry about memory management, then a smart pointer is the best approach. If you are uncomfortable with pointer syntax, then use references.

Adam
I'm afraid of possible memory leaks since I should pass that vector all around. I should definitely get familiar with smart pointers.
Jabba
+1  A: 

You have the best solution. Pass by reference is the way to handle that situation.

tamulj
+4  A: 

Most C++ compilers implement return value optimization which means you can efficiently return a class from a function without the overhead of copying all the objects.

I would also recommend that you write:

vector<int> v(getLargeVector());

So that you copy construct the object instead of default construct and then operator assign to it.

R Samuel Klatchko
std::vector<int> v = getLargeVector();I think that this does the same job and is clearer.
martsbradley
A: 

Sounds like you could do this with a class... but this could be unnecessary.

#include <vector>
using std::vector;

class MySpecialArray
{
    vector<int> v;
public:
    MySpecialArray()
    {
        //fill v
    }
    vector<int> const * getLargeVector()
    {
        return &v;
    }
};
Partial
+1  A: 

RVO can be relied upon to make this code simple to write, but relying RVO can also bite you. RVO is a compiler-dependent feature, but more importantly an RVO-capable compiler can disable RVO depending on the code itself. For example, if you were to write:

MyBigObject Gimme(bool condition)
{
  if( condition )
    return MyBigObject( oneSetOfValues );
  else
    return MyBigObject( anotherSetOfValues );
}

...then even an RVO-capable compiler won't be able to optimize here. There are many other conditions under which the compiler won't be able to optimize, and so by my reckoning any code that by design relies on RVO for performance or functionality smells.

If you buy in to the idea that one function should have one job (I only sorta do), then your dilema as to how to return a populated vector becomes much simpler when you realize that your code is broken at the design level. Your function really does two jobs: it instantiates the vector, then it fills it in. Even with all this pedantary aside, however, a more generic & reliable solution exists than to rely on RVO. Simply write a function that populates an arbitrary vector. For example:

#include <cstdlib>
#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

template<typename Iter> Iter PopulateVector(Iter it, size_t howMany)
{
    for( size_t n = 0; n < howMany; ++n )
    {
        *(it++) = n;
    }

    return it;
}

int main()
{
    vector<int> ints;
    PopulateVector(back_inserter(ints), 42);
    cout << "The vector has " << ints.size() << " elements" << endl << "and they are..." << endl;
    copy(ints.begin(), ints.end(), ostream_iterator<int>(cout, " "));
    cout << endl << endl;

    static const size_t numOtherInts = 42;
    int otherInts[numOtherInts] = {0};
    PopulateVector(&otherInts[0], numOtherInts);
    cout << "The other vector has " << numOtherInts  << " elements" << endl << "and they are..." << endl;
    copy(&otherInts[0], &otherInts[numOtherInts], ostream_iterator<int>(cout, " "));

    return 0;
}
John Dibling