Стандарты возврата векторов в переполнении стека

Теперь я знаю, что это общий вопрос, но я не смог найти прямого ответа на этот вопрос. Это действительно вопрос о стандартах. Я работаю над проектом с использованием генетического алгоритма. Но я сталкиваюсь с узким местом, когда дело доходит до возвращения вектора. Есть ли «правильный» способ сделать это. Обычно я использую динамически размещенные массивы и возвращаю указатель на вновь созданный массив.

obj* func(obj* foo);

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

void func(vector<obj> &input, vector<obj> &result);

И, на заметку для дальнейшего использования, является ли стандартной практикой использование вектора или другого контейнера STL над динамически размещаемым массивом? Используются ли динамически распределенные массивы только как инструмент низкого уровня для проектирования контейнеров? Являются ли они просто пережитком прошлого?

0

Решение

vector<obj> func(const vector<obj>& input);

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

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

Создайте простой и понятный интерфейс, затем измерить производительность и узкие места и оптимизировать оттуда.

В зависимости от вашего шаблона использования, если вы можете повторно использовать выходной контейнер, вы можете преобразовать вышеприведенное в:

void func(vector<obj>& output, const vector<obj>& input);

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

std::vector<obj> output;
for ( ... ) {
output.clear();
func(output, input);

// compare with
for ( ... ) {
std::vector<obj> output = func(input);

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

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

6

Другие решения

Если вы хотите быть уверены, что ваша функция не копирует вектор, то:

 void func(const vector<obj> &input, vector<obj> &result);

Если вы в порядке, полагаясь на «оптимизацию возвращаемого значения» (RTVO) компилятора (большинство хороших компиляторов делают это):

 vector<obj> func(const vector<obj> &input);

Оптимизация возвращаемого значения по существу заключается в том, что компилятор знает, что мы возвращаем копию вектора, поэтому он удаляет «лишнюю копию», которая обычно требуется для возврата содержимого, в данном случае, vector<obj> от func, Компилятор, совместимый с C ++ 11, должен использовать «конструктор перемещения», если он не может выполнять RTVO, что также является небольшой стоимостью во всей схеме.

Если вы счастливы динамически распределить сам вектор, вы можете вернуть указатель на него, но это плохое решение:

vector<obj>* func(const vector<obj> &input);

Это решение основано на распределении vector<obj>, что означает, что что-то где-то должно delete результат звонка func, Поддержание такого кода, если func вызывается много раз, становится довольно противным (особенно если func вызывается много раз, что вполне может иметь место в ситуации генетического анализа).

Обычно трудно добиться возврата ссылки, поэтому вы, вероятно, делаете что-то неправильно, если вы делаете — НЕ ДЕЛАЙТЕ ЭТОГО (если вы не знаете, что делаете и почему):

vector<obj>& func(const vector<obj> &input);

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

Кстати:

obj *func(obj *input)

это кошмар обслуживания, потому что если obj* выделяется в funcтеперь вызывающая сторона должна поддерживать эту структуру.

Наконец, ВСЕГДА, когда вы работаете с производительностью, важно сравнивать с ВАШИМ компилятором на ВАШИХ машинах. Разные компиляторы и разные системы ведут себя по-разному — угадывание, основанное на взгляде на исходный код, является очень плохим решением для понимания того, какой код будет сгенерирован и насколько он быстр.

2

Является ли единственным решением передать «результирующий» вектор по ссылке?

Нет. Вы также можете передать итератор, как это делают алгоритмы:

void func( InputIt first, InputIt last, OutputIt res);

Вы можете сделать этот шаблон:

template< class InputIt, class OutputIt >
void func( InputIt first, InputIt last, OutputIt res);
2

Если можете, всегда используйте ссылку (&) вместо использования самого класса. Это даже относится к умным указателям (std :: shared_ptr), потому что вы избежите лишней копии даже самого указателя. И просто для того, чтобы убедиться, что ваш возвращаемый указатель не перезаписывается, предварительно ожидайте const, так:

void Function(const std::shared_ptr<SomeClass> &someObject);
0
По вопросам рекламы [email protected]