Итак, я довольно опытный программист на 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?
Ответ 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. Это также включает в себя выделение памяти и может быть очень дорогостоящей операцией.