отправка вектора из функции - PullRequest
3 голосов
/ 08 января 2010

Как правильно перевести следующий код Java на C ++?

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

Так что здесь v является ссылкой. Функция создает новый объект Vector и возвращает ссылку на него. Красиво и чисто.

Однако, давайте посмотрим на следующий зеркальный перевод C ++:

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

Теперь v является векторным объектом, и, если я правильно понимаю, v = getLargeVector() будет скопировать всех элементов вектора, возвращаемых функцией в v, что может быть дорого. Кроме того, v2 создается в стеке, и его возврат приведет к другой копии (но, как я знаю, современные компиляторы могут оптимизировать ее).

В настоящее время это то, что я делаю:

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

Но я не считаю это элегантным решением.

Итак, мой вопрос: Как лучше всего это делать (избегая ненужных операций копирования)? Если возможно, я бы хотел избежать обычных указателей. До сих пор я никогда не использовал умные указатели, я не знаю, могут ли они здесь помочь.

Ответы [ 6 ]

6 голосов
/ 08 января 2010

Большинство компиляторов C ++ реализуют оптимизацию возвращаемого значения , что означает, что вы можете эффективно возвращать класс из функции без дополнительных затрат на копирование всех объектов.

Я бы также порекомендовал вам написать:

vector<int> v(getLargeVector());

Таким образом, вы копируете конструкцию объекта вместо конструкции по умолчанию, а затем присваиваете ей оператор.

3 голосов
/ 08 января 2010
void getLargeVector(vector<int>& vec) { 
    // fill the vector
} 

На данный момент это лучший подход. В случае c ++ 0x проблема первого подхода заключается в использовании операций перемещения вместо операций копирования.

2 голосов
/ 08 января 2010

Можно полагаться на RVO, чтобы сделать этот код простым для написания, но использование RVO также может вас укусить. RVO - это функция, зависящая от компилятора, но, что более важно, компилятор с поддержкой RVO может отключить RVO в зависимости от самого кода. Например, если вы напишите:

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

... тогда даже компилятор с поддержкой RVO не сможет оптимизировать здесь. Есть много других условий, при которых компилятор не сможет оптимизировать, и поэтому я считаю любой код, который по своей конструкции полагается на RVO для запахов производительности или функциональности.

Если вы согласитесь с тем, что одна функция должна иметь одну работу (я только это делаю), то ваша дилемма относительно того, как вернуть заполненный вектор, станет намного проще, когда вы поймете, что ваш код нарушен на уровне разработки. , Ваша функция действительно выполняет две работы: она создает вектор, а затем заполняет его. Однако, несмотря на весь этот педантизм, существует более общее и надежное решение, чем полагаться на RVO. Просто напишите функцию, которая заполняет произвольный вектор. Например:

#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;
}
1 голос
/ 08 января 2010

У вас есть лучшее решение. Передача по ссылке - это способ справиться с этой ситуацией.

1 голос
/ 08 января 2010

Почему вы хотите избегать обычных указателей? Это потому, что вы не хотите беспокоиться об управлении памятью или потому, что вы не знакомы с синтаксисом указателя?

Если вы не хотите беспокоиться об управлении памятью, тогда лучше всего использовать умный указатель. Если вас не устраивает синтаксис указателя, используйте ссылки.

0 голосов
/ 08 января 2010

Похоже, вы могли бы сделать это с классом ... но это может быть ненужным.

#include <vector>
using std::vector;

class MySpecialArray
{
    vector<int> v;
public:
    MySpecialArray()
    {
        //fill v
    }
    vector<int> const * getLargeVector()
    {
        return &v;
    }
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...