Перейти к содержимому

Как передать vector в функцию c

  • автор:

Язык C++

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

#include #include #include using namespace std; // Здесь есть проблема void add_item(vectorstring> vec)  vec.push_back("New item!"); > int main()  vectorstring> vec; add_item(vec); for (string s : vec)  cout  <s  <'\n'; > return 0; > 

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

Следующий вариант нашей программы уже будет делать то что мы хотим:

// Здесь есть проблема vectorstring> add_item(vectorstring> vec)  vec.push_back("New item!"); return vec; > int main()  vectorstring> vec; vectorstring> vec2 = add_item(vec); for (string s : vec2)  cout  <s  <'\n'; > return 0; > 

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

Правильное решение нашей задачи в C++ выглядит следующим образом:

void add_item(vectorstring>& vec)  vec.push_back("New item!"); > int main()  vectorstring> vec; add_item(vec); for (string s : vec)  cout  <s  <'\n'; > return 0; > 

Символ амперсанд & позволяет передать в функцию ссылку на параметр. Работа с параметром внутри функции не изменяется, но вместо копии мы имеем дело именно с тем объектом, который был передан в функцию. Таким образом, мы избавились от лишнего копирования и реализовали правильную логику работы программы.

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

// Здесь есть проблема int count_greetings(vectorstring>& vec)  int counter = 0; for (string s : vec)  if (s == "Hello")  ++counter; > > return counter; > 

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

Хорошим стилем в данном случае является передача параметра по константной ссылке:

int count_greetings(const vectorstring>& vec)  int counter = 0; for (const string& s : vec)  if (s == "Hello")  ++counter; > > return counter; > 

Теперь компилятор не позволит изменить объект vec внутри функции count_greetings . Кроме того, теперь в коде явно выражена мысль о том, что объект передается в функцию только для чтения. Такой код проще читать и понимать логику его работы. Обратите внимание, что мы воспользовались константной ссылкой при определении переменной в цикле for . Здесь мы имеем дело с аналогичной ситуацией: в предыдущей версии в переменную s по очереди копировался каждый элемент вектора. Теперь же мы перебираем в цикле константные ссылки на объекты, не копируя их.

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

for (string& s : vec)  s.push_back('!'); > 

Передача константной ссылки на объект в функцию, которая не имеет право изменять объект, имеет смысл только в том случае, если копирование объекта является дорогой операцией. В частности, нет никакого смысла в передаче по ссылке объектов int или double . Это наоборот может привести к потере производительности. Если же мы имеем дело со сложным объектом, таким как string или любым контейнером, то передача по константой ссылке является единственным верным решением.

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

Резюме

Мы обсудили три способа передачи параметров в функцию:

  • передача копии
  • передача по ссылке
  • передача по константной ссылке

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

Источники

  • isocpp.org/wiki/faq/references
  • Введение
  • Настройка рабочей среды
  • Язык C++
    • Работа с потоками ввода-вывода
    • Строки
    • Контейнеры стандартной библиотеки C++
    • Эффективная передача параметров в функцию
    • Алгоритмы стандартной библиотеки C++
    • Итераторы
    • Библиотеки numeric и random
    • Классы
    • Наследование
    • Динамическое выделение памяти
    • Обобщенное программирование

    Передача вектора в качестве аргумента функции

    Здравствуйте, я только недавно начал изучать C++, поэтому проблема может показаться дурацкой, но надеюсь, что поможете.
    Проблема такая, решил для практики написать простую программку, которая запрашивает количество заказанных блюд, их названия и цену, а потом выводит на экран уже с общей суммой.
    Для цен и наименований блюд сделал два вектора, а вывод и подсчёт суммы выделил в отдельную функцию, но компилятор ругается на вызов этой функции и выдаёт ошибку «no matching function for call to «total»». Я пробовал сделать то же уже с обычным массивом и всё заработало, но всё же хочется узнать, что не так я делал с векторами.

    #include #include #include using namespace std; void line() < for (int i=0; icout int total(int kol_vo, string dish[], int cost[]) < int sum=0; line(); for (int i=0; i int main()< cout>kol; vectordishes(kol); vectorcost(kol); for (int i=0; i>dishes[i]; cout>cost[i]; > cout 

    Отслеживать

    219k 15 15 золотых знаков 119 119 серебряных знаков 230 230 бронзовых знаков

    передать вектор в C++11

    Что сказать-то хотел? Предлагаю обсудить релевантые 21 веку языки, где вместо арсенала для стрельбы в ногу ‘should be one— and preferably only one —obvious way to do it’.

    t184256 ★★★★★
    ( 07.04.16 13:30:00 MSK )

     std::vector f()

    Dudraug ★★★★★
    ( 07.04.16 13:53:53 MSK )
    Последнее исправление: Dudraug 07.04.16 13:54:56 MSK (всего исправлений: 2)

    std::vector x(5); 

    (5) здесь не нужно

     std::vector f()

    Ты же понимаешь, да, что второй вызов этой функции к такому же результату не приведёт?

    intelfx ★★★★★
    ( 07.04.16 14:00:28 MSK )

    Давайте устроим конкурс на самую неочевидную строчку кода!

    Dudraug ★★★★★
    ( 07.04.16 14:04:45 MSK )
    Ответ на: комментарий от Dudraug 07.04.16 14:04:45 MSK

    а что в этой строчке неочевидного?

    anonymous
    ( 07.04.16 14:10:29 MSK )
    Ответ на: комментарий от anonymous 07.04.16 14:10:29 MSK

    К тому что в текущем конкретом примере такая конструкция избыточна. Да и не очевидна она. Да, я знаю, что подобны пример есть в оф доке

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

    Dudraug ★★★★★
    ( 07.04.16 14:28:10 MSK )
    Ответ на: комментарий от anonymous 07.04.16 14:10:29 MSK

    Просто вывод через copy, ну я даже не знаю=)

    Как передать vector в функцию c

    Подскажите, как правильно передать std::vector из объекта одного класса в объект другого класса, чтобы не происходило копирование элементов этого вектора? Ниже привожу код, там 2 способа. По этому коду возникают такие вопросы:
    1. Происходит ли копирование элементов вектора в строчке a->vecA = vecB; ?
    2. Что такое указатель на вектор — это адрес управляющей структуры на стеке или адрес первого элемента вектора в куче (т.к. сами данные вектора хранятся в куче)? Если адрес в куче, то будет ли валиден указатель vecAPtr после выполнения функции resize()? Ведь после resize() все элементы вектора vecB могут переместиться на новый адрес.
    3. Если данные способы некорректны, как сделать правильно?

    class MyClassA < public: std::vectorint> vecA; std::vectorint>* vecAPtr; >; class MyClassB < public: MyClassB(MyClassA* aInit) : a(aInit) <> MyClassA* a; std::vectorint> vecB; void doSomething() < for (int i = 0; i < 1000; ++i) < vecB.push_back(i); >a->vecA = vecB; // Происходит копирование всех элементов вектора ? a->vecAPtr = &vecB; vecB.resize(100000000); // Указывает ли vecAPtr сейчас на нужные данные? > >; int main() < MyClassA* a = new MyClassA(); MyClassB* b = new MyClassB(a); b->doSomething(); delete a; delete b; return 0; >

    Re: Как передать вектор между объектами без копирования элементов?

    От: rg45
    Дата: 10.07.12 08:23
    Оценка:

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

    P>Подскажите, как правильно передать std::vector из объекта одного класса в объект другого класса, чтобы не происходило копирование элементов этого вектора? Ниже привожу код, там 2 способа. По этому коду возникают такие вопросы:

    P>1. Происходит ли копирование элементов вектора в строчке a->vecA = vecB; ?

    Происходит.

    P>2. Что такое указатель на вектор — это адрес управляющей структуры на стеке или адрес первого элемента вектора в куче (т.к. сами данные вектора хранятся в куче)? Если адрес в куче, то будет ли валиден указатель vecAPtr после выполнения функции resize()? Ведь после resize() все элементы вектора vecB могут переместиться на новый адрес.

    Указатель на вектор — это указатель на объект, инкапсулирующий указатель (указатели) на данные. Поэтому указатель на вектор не перестает быть действительным после выполнения resize.

    P>3. Если данные способы некорректны, как сделать правильно?

    Не то, чтобы не корректны. Давай подумаем, ты не хочешь, чтобы при передаче вектора от одного объекта к другому происходило копирование его элементов, так? Значит, ты хочешь, чтобы одни и те же данные использовались одновременно несколькими объектами. Для этой цели можно использовать умные указатели shared_ptr и shared_array, которые позволяют нескольким объектам совместно владеть одними данными. Данные будут жить до тех пор, пока у них есть хотя бы один владелец, после чего они автоматически удаляются.


    Не можешь достичь желаемого — пожелай достигнутого.
    Re[2]: Как передать вектор между объектами без копирования элементов?

    От: prrt
    Дата: 10.07.12 09:11
    Оценка:

    R>ты не хочешь, чтобы при передаче вектора от одного объекта к другому происходило копирование его элементов, так?
    Да, именно так.

    R>Для этой цели можно использовать умные указатели shared_ptr и shared_array, которые позволяют нескольким объектам совместно владеть одними данными.
    Спасибо за наводку, проработаю этот момент.

    Re: Как передать вектор между объектами без копирования элементов?

    От: Mr.Delphist
    Дата: 10.07.12 09:21
    Оценка:

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

    P>1. Происходит ли копирование элементов вектора в строчке a->vecA = vecB; ?
    Да

    P>2. Что такое указатель на вектор — это адрес управляющей структуры на стеке или адрес первого элемента вектора в куче (т.к. сами данные вектора хранятся в куче)? Если адрес в куче, то будет ли валиден указатель vecAPtr после выполнения функции resize()? Ведь после resize() все элементы вектора vecB могут переместиться на новый адрес.
    «Указатель на вектор» может указывать на стек, а может на кучу. Например, в Вашем коде vecB создан через new, как часть класса — поэтому лежит в куче. Но кроме вектора, есть ещё память для хранения элементов вектора (в общем случае, тоже можно заполучить как стек, так и кучу, хотя обычно этот буфер на куче). Поэтому после вызова resize() адрес vecB не изменится, но потенциально может измениться адрес vecB[0]

    Re[3]: Как передать вектор между объектами без копирования элементов?

    От: Erop
    Дата: 10.07.12 09:35
    Оценка:

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

    R>>ты не хочешь, чтобы при передаче вектора от одного объекта к другому происходило копирование его элементов, так?
    P>Да, именно так.

    R>>Для этой цели можно использовать умные указатели shared_ptr и shared_array, которые позволяют нескольким объектам совместно владеть одними данными.
    P>Спасибо за наводку, проработаю этот момент.

    Если при этом тебе не надо, что бы в исходном объекте осталась копия данных, то можно через std::swap, например:

     struct A < std::vectorx; >; struct B < std::vectorxx; void GrabXXX( A& a ) < x.clear(); std::swap( a.x, xx ); >>;

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

    Re: Как передать вектор между объектами без копирования элементов?

    От: Сыроежка
    Дата: 10.07.12 10:43
    Оценка:

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

    P>2. Что такое указатель на вектор — это адрес управляющей структуры на стеке или адрес первого элемента вектора в куче (т.к. сами данные вектора хранятся в куче)? Если адрес в куче, то будет ли валиден указатель vecAPtr после выполнения функции resize()? Ведь после resize() все элементы вектора vecB могут переместиться на новый адрес.

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

    Меня можно встретить на www.cpp.forum24.ru
    Re[4]: Как передать вектор между объектами без копирования элементов?

    От: rg45
    Дата: 10.07.12 11:01
    Оценка:

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

    E>Если при этом тебе не надо, что бы в исходном объекте осталась копия данных, то можно через std::swap.

    Или через std::move в новом стандарте:

     struct A < std::vectorx; >; struct B < std::vectorxx; void GrabXXX( A& a ) < a.x = std::move(xx); // передача внутреннего представления вектора без копирования assert(xx.empty()); // xx после этого остается пустым > >;


    Не можешь достичь желаемого — пожелай достигнутого.
    Re: Как передать вектор между объектами без копирования элементов?

    От: Vamp
    Дата: 10.07.12 12:47
    Оценка:

    P>Подскажите, как правильно передать std::vector из объекта одного класса в объект другого класса, чтобы не происходило копирование элементов этого вектора?
    Есть два способа. Первый — передавать собственно вектор по ссылке или по указателю. В этом случае в точном соответствии с условием задачи не происходит копирования вектора, однако возникает вопрос с владением этим указателем на вектор или со временем жизни ссылки.
    Второй — положить в вектор не сами объекты, а (умные) указатели на них. Вектор передавать по значению. В этом случае копирование вектора будет происходить, однако из-за того, что внутри лежит указатель, оно будет быстрым и дешевым. Этот вариант чуть проще с точки зрения управления памятью, однако, следует помнить, что вектора будут разными — например, если добавить элемент в исходный вектор уже после передачи, то во втором векторе его не будет. В зависимости от условий задачи подойдет первый или второй подход.

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

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