Я полностью понимаю, что этот вопрос задали много, но я прошу определенное изменение, и мое поисковое нечто сдалось, поскольку я только нашел алгоритмы, которые добавляют один существующий вектор другому, но не один возвратился к от функции.
У меня есть эта функция, которая перечисляет все файлы в каталоге:
vector<string> scanDir( const string& dir )
который может назвать себя внутренне (для подкаталогов).
Мне нужен короткий способ добавить возвращенное значение к вектору вызывающей стороны. У меня есть в моем уме что-то вроде этого (но конечно он не существует :():
vector<string> fileList;
//...
fileList.append( scanDir(subdirname) );
Я боюсь, что хранение возвращаемого значения и вставка его в списке файлов принесли бы вредность производительности. То, что я имею в виду, является этим:
vector<string> temp( scanDir(subdirname) );
copy( temp.begin(), temp.end(), back_inserter(fileList) );
Спасибо!
PS: я не вынуждаю меня к использованию вектора, любой другой контейнер, который работает одинаково хорошо и может предотвратить потенциальную большую операцию копии, меня устраивает.
Если вы в состоянии изменить scanDir
, сделайте ее (шаблонной) функцией, принимающей выходной итератор:
template <class OutIt>
void scanDir(const std::string& dirname, OutIt it) {
// ...
// Scan subdir
scanDir(subdir, it);
// ...
}
У вас будет дополнительное преимущество - возможность заполнять всевозможные структуры данных, такие как
std::vector<string> vector;
scanDir(dir1, std::back_inserter(vector));
std::set<string> fileset
scanDir(dir1, std::inserter(fileset, fileset.begin()));
и т.д.
EDIT (см. комментарий ...)
Для использования этой функции для инициализации членов класса, вы можете либо вызвать ее в конструкторе, как в
class MyClass {
private:
std::vector<string> m_fileList;
public:
MyClass(const std::string& dirname) {
scanDir(dirname, std::back_inserter(m_fileList);
}
}
или использовать функцию-обертку
std::vector<string> scanDir(const std::string& dirname) {
std::vector<string> result;
scanDir(dirname, std::back_inserter(result);
return result;
}
class MyClass {
// Same as above..
MyClass(const std::string& dirname) : m_fileList(scanDir(dirname)) { }
}
Я бы предпочел первый вариант по причинам производительности (и другим) ...
Почему бы просто не передавать вектор в качестве аргумента? Тогда каждый вызов сможет добавлять к одному и тому же вектору, без копирования. Или создать класс реализации, который накапливает элементы в объект-член.
PS: Я не заставляю себя использовать вектор, меня устраивает любой другой контейнер, который работает одинаково хорошо и может предотвратить потенциальную операцию большого копирования.
Что ж, если вы воспользуетесь списком
и вызовете a.splice (a.end (), b);
, вы полностью избежите операции копирования. Список
обычно будет связанным списком, а не массивом, как в случае с вектором
, поэтому это имеет большое значение для производительности и использования. Но монтаж выполняется за O (1), так что это приятное преимущество.
Рекурсивная функция должна будет копировать все несколько раз, если быть точным, O (глубина) (то есть все на конечном уровне будет копироваться снова и снова, пока не достигнет корня).
Лучшим способом было бы разделить это на две разные функции:
vector<string> scanDir(string path)
{
vector<string> retval;
scanDir(path, &retval);
return retval;
}
static void scanDir(string path, vector<string>& output)
{
.. scan
.. append to output
}
Используйте std :: list и добавьте с помощью std :: list :: splice.
Операция не включает в себя создание или уничтожение какого-либо объекта-элемента и, за исключением третьей версии, выполняется в постоянное время.
Вместо
vector<string> temp( scanDir(subdirname) );
вы можете ввести
vector<string> const& temp = scanDir(subdirname);
и продолжить копирование:
fileList.insert(fileList.end(), temp.begin(), temp.end());
Я знаю, что это не дает прямого ответа на ваш вопрос, но, что касается вашей основной цели, вы можете просто переопределить свою функцию в терминах boost :: filesystem. Итератор каталогов уже является рекурсивным, поэтому вам не нужно выполнять собственные рекурсивные вызовы. Вы можете просто заполнить список в цикле через итератор. Вот пример реализации ls: http://www.boost.org/doc/libs/1_43_0/libs/filesystem/example/simple_ls.cpp
Вы также получаете дополнительное преимущество (теоретической) независимости от платформы, относительно широкое распространение (ошибки становятся выявляется быстрее с большим количеством усыновлений) и т. д.
Возможно, это не самое простое решение, но как насчет того, чтобы сделать что-то эквивалентное StringBuilder в C #?
Создайте list
, тогда вы сможете добавьте в список все векторы, полученные в результате вызовов scanDir ()
.
Если вам абсолютно необходимо иметь единственный вектор в конце, вы можете, как только создать новый вектор, выделить его достаточно большим, чтобы не нужно было изменять его размер, и собрать готовый продукт.
В качестве альтернативы вы можете создать новый класс (при необходимости, производный от vector
Как насчет вспомогательной функции?
template<class T>
std::vector<T>& VectorAppend(std::vector<T> &target, const std::vector<T> &source)
{
size_t insertPos = target.size();
target.resize(target.size() + source.size());
std::copy(source.begin(), source.end(), target.begin() + insertPos);
return target;
}
vector<string> fileList;
vector<string> temp( scanDir(subdirname) );
fileList.insert(fileList.end(), temp.begin(), temp.end());
Надеюсь, это вам помогло.