Как вернуть вектор из функции c
Перейти к содержимому

Как вернуть вектор из функции c

  • автор:

Как вернуть vector из функции: по значению или по ссылке?

Есть функция, создающая каким-то определенным образом экземпляр vector . Вопрос: как вернуть этот экземпляр вызывающему?

Правильное с точки зрения логики и стройности программы решение выглядит так:

std::vectorint> create_vector(const size_t N) std::vectorint> v; v.resize(N, 0xDEADC0DE); return v; > 

Тут экземпляр вектора возвращается по значению, что означает потенциальное глубокое копирование локального объекта в контекст вызывающей функции. Сразу возникает сомнение: а что, если вектор огромен — его ж надо будет побайтно перекладывать из одного места в другое? Гораздо “разумнее” было бы написать:

void create_vector(const size_t N, std::vectorint>* v) v->resize(N, 0xDEADC0DE); > 

Тут вектор передается по указателю, и стопроцентно ненужного полного копирования не будет. Но такой код выглядит откровенно плохо.

Сравним скорости работы на векторе длиной 100MB. Например, на компиляторе:

Apple clang version 3.1 (tags/Apple/clang-318.0.45) (based on LLVM 3.1svn) Target: x86_64-apple-darwin11.3.0 
#include #include std::vectorint> __attribute__((noinline)) create_vector(const size_t N) std::cout  <"by value"  ::endl; std::vectorint> v; v.resize(N, 0xDEADC0DE); return v; > int main(int argc, char* argv[]) for (size_t i = 0; i  10; ++i) const size_t N = 1024 * 1024 * 100; std::vectorint> v = create_vector(N); if (v[i] != 0xDEADC0DE) std::cout  <"Test is rubbish"  ::endl; return 0; > > return 0; > 
clang++ -O3 -o by_value by_value.cpp && time ./by_value 
0m4.933s 

Теперь по указателю:

#include #include void __attribute__((noinline)) create_vector(const size_t N, std::vectorint>* v) std::cout  <"by pointer"  ::endl; v->resize(N, 0xDEADC0DE); > int main(int argc, char* argv[]) for (size_t i = 0; i  10; ++i) const size_t N = 1024 * 1024 * 100; std::vectorint> v; create_vector(N, &v); if (v[i] != 0xDEADC0DE) std::cout  <"Test is rubbish"  ::endl; return 0; > > return 0; > 
clang++ -O3 -o by_pointer by_pointer.cpp && time ./by_pointer 
0m4.852s 

Время в обоих случаях одинаково. Получается, что стоит выбрать первый, “красивый” вариант.

Объяснений тут два. Первый, и возможно самый важный — это RVO, Return value optimization. Это когда компилятор догадывается, что создаваемый локальный экземпляр вектора предназначен для возврата из функции, и сразу создает его в контексте вызывающего кода, чтобы потом не копировать туда. Фактически компилятор реализует передачу по ссылке, но неявно, не портя красоту исходного кода. Данный трюк будет работать для любого класса, не обязательно класса из STL.

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

Ну а в контексте C++11, где есть семантика перемещения, вообще не будет лишних копирований, если класс “правильно” реализован (что верно для классов из STL).

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

Возвращаемый вектор из функции C++

Я пытаюсь вернуть вектор со значениями в обратном порядке (например, я ввожу вектор с 0,1,2,3,4 и функция возвращает вектор с 4,3,2,1,0).

Компилятор говорит: Segmentation fault.

В моих тестах я заметил, что, вероятно, моя проблема заключается в присваивании new2 = ret_vec(числа); , но я не знаю, что происходит.

#include #include #include using namespace std; vector ret_vec(vectorn) < vector n2; for (int i = 0; i < n.size(); ++i)< n2[i] = n[i]; >return n2; > void initializer(int s, vector& n) < for (int i = 0; i< s; ++i)< n.push_back(i); >> void print_vector(vector n) < for (int i = 0; i> int main () < vector numbers; int size; cin >> size; initializer(size,numbers); vector new2(numbers.size()); cout

Поделиться Источник 13 мая 2015 в 21:11

2 ответа

Я пытаюсь вернуть вектор со значениями в обратном порядке

Самый простой подход — использовать C++:

vector reverse_vec(const vector& n) < vectorret(n.rbegin(), n.rend()); return ret; > 

Здесь обратные итераторы используются для создания вектора с содержимым входного вектора, обращенного в обратном порядке. В качестве альтернативы, если вы хотите обратить вектор на месте, используйте std::reverse :

vector v = . std::reverse(v.begin(), v.end()); // v is now reversed 

Что касается проблем с вашим кодом, очевидным является то, что вы получаете доступ к вектору вне границ здесь:

vector n2; // empty vector for (int i = 0; i < n.size(); ++i)< n2[i] = n[i]; // oops! n2 accessed out of bounds! >

Исправление заключается в том, чтобы не использовать этот код вообще и искать простой вариант.

Поделиться 13 мая 2015 в 21:12

В следующей функции

vector ret_vec(vectorn) < vector n2; for (int i = 0; i < n.size(); ++i)< n2[i] = n[i]; >return n2; > 

Вы просто копируете содержимое вектора параметров. (Я думаю, вы забыли пробел между параметром и типом его)

Вы также можете изменить порядок таким образом (его можно изменить «по рукам»):

vector ret_vec(vector n) < vector n2; for(int i=n1.size()-1; i return n2; > 

Как вернуть вектор из функции c

Для добавления элементов в вектор применяется функция push_back() , в которую передается добавляемый элемент:

#include #include int main() < std::vectornumbers; // пустой вектор numbers.push_back(5); numbers.push_back(3); numbers.push_back(10); for(int n : numbers) cout << n << "\t"; // 5 3 10 std::cout

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

Функция emplace_back() выполняет аналогичную задачу — добавляет элемент в конец контейнера:

std::vector numbers< 1, 2, 3, 4, 5 >; numbers.emplace_back(8); // numbers = < 1, 2, 3, 4, 5, 8 >;

Добавление элементов на определенную позицию

Ряд функций позволяет добавлять элементы на определенную позицию.

  • emplace(pos, value) : вставляет элемент value на позицию, на которую указывает итератор pos
  • insert(pos, value) : вставляет элемент value на позицию, на которую указывает итератор pos, аналогично функции emplace
  • insert(pos, n, value) : вставляет n элементов value начиная с позиции, на которую указывает итератор pos
  • insert(pos, begin, end) : вставляет начиная с позиции, на которую указывает итератор pos, элементы из другого контейнера из диапазона между итераторами begin и end
  • insert(pos, values) : вставляет список значений начиная с позиции, на которую указывает итератор pos
std::vector numbers< 1, 2, 3, 4, 5 >; auto iter = numbers.cbegin(); // константный итератор указывает на первый элемент numbers.emplace(iter + 2, 8); // добавляем после второго элемента numbers = < 1, 2, 8, 3, 4, 5>;
std::vector numbers1< 1, 2, 3, 4, 5 >; auto iter1 = numbers1.cbegin(); // константный итератор указывает на первый элемент numbers1.insert(iter1 + 2, 8); // добавляем после второго элемента //numbers1 = < 1, 2, 8, 3, 4, 5>; std::vector numbers2 < 1, 2, 3, 4, 5 >; auto iter2 = numbers2.cbegin(); // константный итератор указывает на первый элемент numbers2.insert(iter2 + 1, 3, 4); // добавляем после первого элемента три четверки //numbers2 = < 1, 4, 4, 4, 2, 3, 4, 5>; std::vector values < 10, 20, 30, 40, 50 >; std::vector numbers3 < 1, 2, 3, 4, 5 >; auto iter3 = numbers3.cbegin(); // константный итератор указывает на первый элемент // добавляем после первого элемента три первых элемента из вектора values numbers3.insert(iter3 + 1, values.begin(), values.begin() + 3); //numbers3 = < 1, 10, 20, 30, 2, 3, 4, 5>; std::vector numbers4 < 1, 2, 3, 4, 5 >; auto iter4 = numbers4.cend(); // константный итератор указывает на позицию за последним элементом // добавляем в конец вектора numbers4 элементы из списка < 21, 22, 23 >numbers4.insert(iter4, < 21, 22, 23 >); //numbers4 = < 1, 2, 3, 4, 5, 21, 22, 23>;

Удаление элементов

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

std::vector v < 1,2,3,4 >; v.clear();

Функция pop_back() удаляет последний элемент вектора:

std::vector v < 1,2,3,4 >; v.pop_back(); // v =

Если нужно удалить элемент из середины или начала контейнера, применяется функция std::erase() , которая имеет следующие формы:

  • erase(p) : удаляет элемент, на который указывает итератор p. Возвращает итератор на элемент, следующий после удаленного, или на конец контейнера, если удален последний элемент
  • erase(begin, end) : удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, или на конец контейнера, если удален последний элемент

std::vector numbers1 < 1, 2, 3, 4, 5, 6 >; auto iter = numbers1.cbegin(); // указатель на первый элемент numbers1.erase(iter + 2); // удаляем третий элемент // numbers1 = < 1, 2, 4, 5, 6 >std::vector numbers2 = < 1, 2, 3, 4, 5, 6 >; auto begin = numbers2.cbegin(); // указатель на первый элемент auto end = numbers2.cend(); // указатель на последний элемент numbers2.erase(begin + 2, end — 1); // удаляем с третьего элемента до последнего // numbers2 =

Также начиная со стандарта С++20 в язык была добавлена функция std::erase() . Она не является частью типа vector. В качестве первого параметра она принимает вектор, а в качестве второго — элемент, который надо удалить:

std::vector numbers3 < 1, 2, 3, 1, 5, 6 >; std::erase(numbers3, 1); // numbers3 =

В данном случае удаляем из вектора numbers3 все вхождения числа 1.

Размер вектора

С помощью функции size() можно узнать размер вектора, а с помощью функции empty() проверить, путой ли вектор:

#include #include int main() < std::vectornumbers; if(numbers.empty()) std::cout

С помощью функции resize() можно изменить размер вектора. Эта функция имеет две формы:

  • resize(n) : оставляет в векторе n первых элементов. Если вектор содержит больше элементов, то его размер усекается до n элементов. Если размер вектора меньше n, то добавляются недостающие элементы и инициализируются значением по умолчанию
  • resize(n, value) : также оставляет в векторе n первых элементов. Если размер вектора меньше n, то добавляются недостающие элементы со значением value

std::vector numbers1 < 1, 2, 3, 4, 5, 6 >; numbers1.resize(4); // оставляем первые четыре элемента — numbers1 = numbers1.resize(6, 8); // numbers1 =

Важно учитывать, что применение функции resize может сделать некорректными все итераторы, указатели и ссылки на элементы.

Изменение элементов вектора

Функция assign() позволяет заменить все элементы вектора определенным набором:

std::vector langs = < "Java", "JavaScript", "C">; langs.assign(4, «C++»); // langs =

В данном случае элементы вектора заменяются набором из четырех строк «C++».

Также можно передать непосредственно набор значений, который заменит значения вектора:

std::vector langs< "Java", "JavaScript", "C">; langs.assign(< "C++", "C#", "C">); // langs =

Еще одна функция — swap() обменивает значения двух контейнеров:

std::vector clangs < "C++", "C#", "Java" >; std::vector ilangs < "JavaScript", "Python", "PHP">; clangs.swap(ilangs); // clangs = < "JavaScript", "Python", "PHP">; for(std::string lang : clangs)

Сравнение векторов

Векторы можно сравнивать — они поддерживают все операции сравнения: , =, ==, !=. Сравнение контейнеров осуществляется на основании сравнения пар элементов на тех же позициях. Векторы равны, если они содержат одинаковые элементы на тех же позициях. Иначе они не равны:

std::vector v1 ; std::vector v2 ; std::vector v3 ; bool v1v2 = v1 == v2; // true bool v1v3 = v1 != v3; // true bool v2v3 = v2 == v3; // false

Как вернуть вектор из функции c

Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?

vector function() < vectorfoo; // заполнение вектора. return foo; >
void function( vector & foo ) < // заполнение вектора. >

Re: Как вернуть из функции vector<>

От: Tan4ik
Дата: 18.03.04 10:22
Оценка: -1

Здравствуйте, Аноним, Вы писали:

А>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?

Удалено избыточное цитирование — WH

Первый вариант нагляднее и удобнее. Им надо пользоваться, если данное место точно не является «узким». Если же есть подозрения на «узкость», то второй метод предпочтительнее, т.к. быстрее.


С уважением,
Лазарев Андрей
Re: Как вернуть из функции vector<>

От: Vamp
Дата: 18.03.04 10:23
Оценка:

Лучше, конечно, по второму варианту.
Да здравствует мыло душистое и веревка пушистая.
Re: Как вернуть из функции vector<>

От: LaptevVV
Дата: 18.03.04 10:25
Оценка:

Здравствуйте, Аноним, Вы писали:

А>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?

Удалено избыточное цитирование — WH

Оба правильные. Я для наглядности использую первый. а для эффективности — второй.
Хочешь быть счастливым — будь им!
Без булдырабыз.
Re: Как вернуть из функции vector<>

От: Bell
Дата: 18.03.04 10:27
Оценка:

Здравствуйте, Аноним, Вы писали:

Ну, если тебя не пугает создание/копирование/разрушение временного вектора, и всех содержащихся в нем объектов, то можно выбрать вариант 1.
А если это по каким-то причинам нежелательно — то тогда уж вариант 2.

Любите книгу — источник знаний (с) М.Горький
Re: Как вернуть из функции vector<>

От: FreshMeat http://www.rsdn.org
Дата: 18.03.04 10:52
Оценка:

Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, Все!

А>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?
Если объекты действительно громоздкие, то в контейнере их лучше не хранить (только указатели на них)
Если громоздкий сам контейнер, то проще возвращать std::auto_ptr < std::vector>

Хорошо там, где мы есть! 🙂
Re[2]: Как вернуть из функции vector<>

От: Denis http://blogs.gotdotnet.ru/personal/Denis
Дата: 18.03.04 11:07
Оценка:

А>>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?
FM>Если объекты действительно громоздкие, то в контейнере их лучше не хранить (только указатели на них)
FM>Если громоздкий сам контейнер, то проще возвращать std::auto_ptr < std::vector>

The semantics of auto_ptr always include ownership, so don’t use auto_ptrs in a parameter list or as a return value if you don’t mean to transfer ownership

//this is a bad example template class T> void bad_print(std::auto_ptr p) //p gets ownership of passed argument < //does p own an object ? if (p.get() == NULL) < std::cout "NULL"; > else < std::cout > //Oops, exiting deletes the object to which p refers

кажется лучше так не делать как вы предложили

Re: Как вернуть из функции vector<>

От: jazzer Skype: enerjazzer
Дата: 18.03.04 11:17
Оценка:

Здравствуйте, Аноним, Вы писали:

А>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?

Удалено избыточное цитирование — WH

если ты переобзовешь твою function во что-нибудь типа fill_vector, то вопрос отпедет сам собой.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got If you always do what you always did

Re[3]: Как вернуть из функции vector<>

От: FreshMeat http://www.rsdn.org
Дата: 18.03.04 11:24
Оценка:

Здравствуйте, Denis, Вы писали:

D>возьмём Джосатиса:
D>

D>The semantics of auto_ptr always include ownership, so don’t use auto_ptrs in a parameter list or as a return value if you don’t mean to transfer ownership

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

. if you don’t mean to transfer ownership

Хорошо там, где мы есть! 🙂
Re: Как вернуть из функции vector<>

От: Kluev
Дата: 18.03.04 11:30
Оценка: +1

А>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?

Правильней всегда так:

А>

А>void function( vector & foo ) А> < А>// заполнение вектора. А>> А>

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

Re[4]: Как вернуть из функции vector<>

От: Denis http://blogs.gotdotnet.ru/personal/Denis
Дата: 18.03.04 11:33
Оценка:

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

Re: Как вернуть из функции vector<>

От: Кодт
Дата: 18.03.04 11:57
Оценка: 15 (1)

Здравствуйте, Аноним, Вы писали:

А>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?

На выбор:
* out-параметром: void foo(vector & elephants)
* умным указателем: auto_ptr foo()
* реализовать стратегию move constructor (собственно, auto_ptr это и делает).

Последнее выглядит примерно так:

#include // только ради std::swap() #include using namespace std; // утилитка для трассировки входа-выхода в функции struct func < const char* name; func(const char* n) : name(n) < cout "enter " << name "()" ~func() < cout "leave " << name "()" >; /////////////////////////////////////////////// // классы реализующие стратегию move ctor templateclass T> struct movector_src_t; templateclass T> struct movector_dst_t < T& var; movector_dst_t(T& v) : var(v) <>void operator=(const movector_src_t& src) const < swap(var, src.value); >>; templateclass T> movector_dst_t movector_dst(T& var) < // func _("movctor_dst"); return movector_dst_t(var); > templateclass T> struct movector_src_t < mutable T value; movector_src_t(T& v) < swap(value, v); >movector_src_t(const movector_src_t& src) < swap(value, src.value); >>; templateclass T> movector_src_t movector_src(T& var) < // func _("movctor_src"); return movector_src_t(var); > templateclass T> movector_src_t movector_src_value(T value) < // func _("movctor_src_value"); return movector_src_t(value); > // /////////////////////////////////////////////// /////////////////////////////////////////////// // тяжеловесный класс (работает со строками) struct elephant_t < char* str; elephant_t() : str(strdup("null")) < cout this "->ctor()" elephant_t(const char* s) : str(strdup(s)) < cout this "->ctor(" << str ")" elephant_t(const elephant_t& src) : str(strdup(src.str)) < cout this "->ctor(" << &src ") ->operator=(" << &src ")" ~elephant_t() < cout this "->dtor() swap(" << &a ", " << &b ")" // /////////////////////////////////////////////// /////////////////////////////////////////////// // песочница movector_src_t foo(bool lr) < func _("foo"); elephant_t e("fooooooooo"); cout "foo() done" if(lr) return movector_src(e); else return movector_src_value( elephant_t("vaaaaaaluuuuueee") ); > void bar() < func _("bar"); elephant_t e("barrrr"); cout "bar() calls foo(l)" true); cout << endl; cout "bar() calls foo(r)" false); cout << endl; cout "bar() done" // /////////////////////////////////////////////// int main() < bar(); return 0; >

естественно, для этого нужно, чтобы swap() был эффективным.
Для контейнеров STL это возможно, хотя и необязательно.
Во всяком случае, для VC6/Dinkumware swap векторов выполняет копирование, а не обмен внутренних структур.

Перекуём баги на фичи!
Re[3]: Как вернуть из функции vector<>

От: Кодт
Дата: 18.03.04 12:01
Оценка: +2

Здравствуйте, Denis, Вы писали:

А>>>Подскажите, пожалуйста, как правильней вернуть из функции значение, являющееся вектором (возможно достаточно громоздких объектов)?
FM>>Если объекты действительно громоздкие, то в контейнере их лучше не хранить (только указатели на них)
FM>>Если громоздкий сам контейнер, то проще возвращать std::auto_ptr < std::vector>

D>возьмём Джосатиса:
D>

D>The semantics of auto_ptr always include ownership, so don’t use auto_ptrs in a parameter list or as a return value if you don’t mean to transfer ownership

D>//this is a bad example D> template class T> D> void bad_print(std::auto_ptr p) //p gets ownership of passed argument D> < D>//does p own an object ? D> if (p.get() == NULL) < D>std::cout "NULL"; D> > D> else < D>std::cout > D> > //Oops, exiting deletes the object to which p refers D>

D>кажется лучше так не делать как вы предложили

Ты перепутал. В коде который приводишь ты, auto_ptr является in-параметром, что действительно опасно.
А вот возвращать — это сколько угодно.

auto_ptr good_job() < return auto_ptr(new Some); > void accept_some() < good_job(); // delete возвращённое значение good_job()->method(); // сперва temp->method, а затем delete < auto_ptrdst = good_job(); // передача владения . > // delete >

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *