Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Вопросы о том, как работает передача аргументов в C++

Автор вопроса: sicklybeansОтветов: 4

Итак, я довольно опытный программист на C, которому приходится много программировать на C++. В языке есть некоторые тонкости, в которых я никогда не чувствовал себя уверенным. Например, лучшие методы передачи аргументов.

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

void fn(vector<Object> vec);
void fn(vector<Object*> vec);
void fn(vector<Object> *vec);
void fn(vector<Object> &vec);

И, конечно же, мы могли бы также взять некоторую комбинацию этих функций.

Я хочу убедиться, что у меня есть это прямо:

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

(в этом я не уверен) Метод 2 скопирует все переменные метода vec, но скопирует только адреса каждого из экземпляров объекта. Я недостаточно знаю о том, что содержится в векторном классе, чтобы понять, целесообразно это или нет.

Методы 3 и 4 довольно просты и похожи друг на друга и требуют минимальных накладных расходов.

Все это правильно? и какой метод предпочтительнее, учитывая, что мы ничего не знаем о классе Object?

  • Предпочтительный метод зависит от того, что вы хотите сделать, но если вы не хотите изменять вектор и не хотите копировать, то обычным подходом будет передача ссылки const: void fn(const vecctor<Object>& vec);. juanchopanza 06.08.2013
  • возможный дубликат Как правильно передать параметры? - за исключением того, что ваш второй вариант не покрыто там, потому что это не совсем связано stijn 06.08.2013

c++argumentsreferencevector

Ответы

Ответ 1 · рейтинг 3 · aaronman

Предпочтительным способом передачи аргумента будет
void fn(const vector<Object> &vec);
При условии, конечно, что вы его не изменяете. Использование указателей, как в случае 2 и 3, обычно не является хорошей практикой в ​​C++.
Теперь версия 1:
void fn(vector<Object> vec);
Обычно считается плохой, потому что она создает копию, но иногда это необходимо. Имейте в виду, что иногда компилятор может оптимизировать копию, используя исключение копии в случае временные и если копия не изменена (но в этом случае вы должны использовать константную ссылку)

Ответ 2 · рейтинг 1 · JoshG79

Все, что вы говорите, правильно.

Если вы все равно собираетесь копировать вектор внутри функции или если вы хотите изменить vec внутри fn, но не изменять исходную копию, используйте #1. Затем вы можете сохранить себе копию внутри и просто позволить компилятору (возможно) сделать копию за вас.

Если вы хотите изменить исходный вектор (извне fn), используйте #4.

Если вы вообще не хотите изменять вектор, используйте #4 с const:

void fn(const vector<Object> &vec);

Не используйте № 2, потому что вам будет очень трудно поместить указатель в вектор. Память должна быть newd и deleted, и она будет подвержена ошибкам. [Обязательное примечание shared_ptr].

Ваш № 3 не является идиоматическим С++. Это нормально, но в этом случае идиоматический C++ избегает указателей в пользу ссылок.

Ответ 3 · рейтинг 1 · Dima

Это все правильно. Метод 2 действительно сделает копию vec, и, поскольку его элементы являются указателями на Object, он будет копировать только эти указатели, а не фактические объекты. Это не сильно отличается от передачи массива указателей на Object.

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

Если объекты маленькие, вы должны использовать вектор объектов, а не указатели. Если объекты большие, вы должны использовать вектор интеллектуальных указателей.

Ответ 4 · Cassio Neri

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

1) Если у вас есть

std::vector<Object> v;

тогда, очевидно, вы не можете вызвать fn(v) для перегрузки void fn(vector<Object*> vec);.

2) Вы сказали:

"Я недостаточно знаю, что содержится в векторном классе"...

Наиболее популярные реализации std::vector имеют только три указателя в качестве элементов данных, и стоимость копирования этих трех указателей очень мала. Однако это не то, что происходит, когда вы копируете std::vector (это было бы, если бы его конструктор копирования был сгенерирован компилятором). На самом деле std::vector имеет конструктор копирования, который (как вы сказали) копирует все элементы исходного std::vector. Это также включает в себя выделение памяти и может быть очень дорогостоящей операцией.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы