Подтвердить что ты не робот

Вернуть вектор vs использовать параметр для вектора, чтобы вернуть его

С приведенным ниже кодом возникает вопрос:

Если вы используете функцию 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;
}
4b9b3361

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

Я думаю, что стоит упомянуть еще один или два других момента. Во-первых, возврат объекта официально копирует объект; даже если компилятор оптимизирует код, чтобы он никогда не выполнялся, он все равно не будет (или, по крайней мере, не должен) работать, если копия 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"));

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

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