вернуть вектор против использования параметра для вектора, чтобы вернуть его - PullRequest
15 голосов
/ 23 августа 2011

С кодом ниже, вопрос:

Если вы используете функцию returnIntVector (), копируется ли вектор из локальной во «внешнюю» (глобальную) область? Другими словами, это больше времени и памяти, потребляющее вариацию по сравнению с функцией "getIntVector ()"? (Однако, предоставляя ту же функциональность.)

#include <iostream>
#include <vector>
using namespace std;

vector<int> returnIntVector()
{
   vector<int> vecInts(10);
   for(unsigned int ui = 0; ui < vecInts.size(); ui++)
      vecInts[ui] = ui;

   return vecInts;
}

void getIntVector(vector<int> &vecInts)
{
   for(unsigned int ui = 0; ui < vecInts.size(); ui++)
      vecInts[ui] = ui;
}

int main()
{
   vector<int> vecInts = returnIntVector();
   for(unsigned int ui = 0; ui < vecInts.size(); ui++)
      cout << vecInts[ui] << endl;
   cout << endl;

   vector<int> vecInts2(10);
   getIntVector(vecInts2);
   for(unsigned int ui = 0; ui < vecInts2.size(); ui++)
      cout << vecInts2[ui] << endl;

   return 0;
}

Ответы [ 6 ]

18 голосов
/ 23 августа 2011

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

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

9 голосов
/ 23 августа 2011

Используйте первую форму: ту, которая возвращает вектор. И хороший компилятор, скорее всего, оптимизирует его. Оптимизация обычно известна как Оптимизация возвращаемого значения , или RVO, короче.

4 голосов
/ 23 августа 2011

Другие уже указывали, что при достойном (не великом, просто приличном) компиляторе оба обычно заканчивают тем, что производят идентичный код, поэтому оба дают эквивалентную производительность.

Я думаю, что стоит упомянуть один или два других момента. Во-первых, возвращение объекта официально копирует объект; даже если компилятор оптимизирует код так, чтобы копирование никогда не происходило, оно все равно не будет (или, по крайней мере, не должно) работать, если ctor для этого класса недоступен. std::vector, безусловно, поддерживает копирование, , но вполне возможно создать класс, который вы сможете изменить, как в getIntVector, но не вернуть, как в returnIntVector.

Во-вторых, и, что гораздо важнее, я бы вообще рекомендовал не использовать или из них. Вместо того, чтобы передавать или возвращать (ссылку на) вектор, вы обычно должны работать с итератором (или двумя). В этом случае у вас есть пара совершенно разумных вариантов - вы можете использовать либо специальный итератор, либо создать небольшой алгоритм. Версия итератора будет выглядеть примерно так:

#ifndef GEN_SEQ_INCLUDED_
#define GEN_SEQ_INCLUDED_

#include <iterator>

template <class T>
class sequence : public std::iterator<std::forward_iterator_tag, T>
{ 
    T val;
public:
    sequence(T init) : val(init) {}
    T operator *() { return val; }
    sequence &operator++() { ++val; return *this; }
    bool operator!=(sequence const &other) { return val != other.val; }
};

template <class T>
sequence<T> gen_seq(T const &val) {
    return sequence<T>(val);
}

#endif

Вы бы использовали это примерно так:

#include "gen_seq"

std::vector<int> vecInts(gen_seq(0), gen_seq(10));

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

Альтернатива алгоритма будет выглядеть примерно так:

template <class T, class OutIt>
class fill_seq_n(OutIt result, T num, T start = 0) {
    for (T i = start; i != num-start; ++i) {
        *result = i;
        ++result;
    }
}

... и вы бы использовали что-то вроде этого:

std::vector<int> vecInts;
fill_seq_n(std::back_inserter(vecInts), 10);

Вы также можете использовать функциональный объект с std::generate_n, но, по крайней мере, IMO, это обычно приводит к большим проблемам, чем оно того стоит.

Пока мы говорим о таких вещах, я бы также заменил это:

for(unsigned int ui = 0; ui < vecInts2.size(); ui++)
    cout << vecInts2[ui] << endl;

... с чем-то вроде этого:

std::copy(vecInts2.begin(), vecInts2.end(), 
          std::ostream_iterator<int>(std::cout, "\n"));
0 голосов
/ 23 августа 2011

В C ++ 03 дня, getIntVector () рекомендуется для большинства случаев. В случае returnIntVector () он может создать несколько ненужных временных.

Но с помощью оптимизации возвращаемого значения и swaptimization большинство из них можно избежать. В эпоху C ++ 11 последнее может быть значимым из-за семантики перемещения.

0 голосов
/ 23 августа 2011

Теоретически, функция returnIntVector возвращает вектор по значению, поэтому будет сделана копия, и это займет больше времени, чем функция, которая просто заполняет существующий вектор. Больше памяти также будет использоваться для хранения копии, но только временно; так как vecInts локально ограничен, он будет выделен в стеке и будет освобожден, как только вернется returnIntVector. Однако, как отмечали другие, современный компилятор оптимизирует эту неэффективность.

0 голосов
/ 23 августа 2011

returnIntVector отнимает больше времени, поскольку возвращает копию вектора, если реализация вектора не реализована с одним указателем, в этом случае производительность одинакова.

в целомВы не должны полагаться на реализацию и использовать вместо нее getIntVector .

...