C++ добавляет один вектор другому

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

У меня есть эта функция, которая перечисляет все файлы в каталоге:

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: я не вынуждаю меня к использованию вектора, любой другой контейнер, который работает одинаково хорошо и может предотвратить потенциальную большую операцию копии, меня устраивает.

9
задан rubenvb 20 July 2010 в 19:20
поделиться

10 ответов

Если вы в состоянии изменить 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)) { }
}

Я бы предпочел первый вариант по причинам производительности (и другим) ...

10
ответ дан 4 December 2019 в 06:29
поделиться

Почему бы просто не передавать вектор в качестве аргумента? Тогда каждый вызов сможет добавлять к одному и тому же вектору, без копирования. Или создать класс реализации, который накапливает элементы в объект-член.

15
ответ дан 4 December 2019 в 06:29
поделиться

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

Что ж, если вы воспользуетесь списком и вызовете a.splice (a.end (), b); , вы полностью избежите операции копирования. Список обычно будет связанным списком, а не массивом, как в случае с вектором , поэтому это имеет большое значение для производительности и использования. Но монтаж выполняется за O (1), так что это приятное преимущество.

8
ответ дан 4 December 2019 в 06:29
поделиться

Рекурсивная функция должна будет копировать все несколько раз, если быть точным, 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 
}
0
ответ дан 4 December 2019 в 06:29
поделиться

Используйте std :: list и добавьте с помощью std :: list :: splice.

Из документации для монтажа :

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

2
ответ дан 4 December 2019 в 06:29
поделиться

Вместо

vector<string> temp( scanDir(subdirname) );

вы можете ввести

vector<string> const& temp = scanDir(subdirname);

и продолжить копирование:

fileList.insert(fileList.end(), temp.begin(), temp.end());
1
ответ дан 4 December 2019 в 06:29
поделиться

Я знаю, что это не дает прямого ответа на ваш вопрос, но, что касается вашей основной цели, вы можете просто переопределить свою функцию в терминах boost :: filesystem. Итератор каталогов уже является рекурсивным, поэтому вам не нужно выполнять собственные рекурсивные вызовы. Вы можете просто заполнить список в цикле через итератор. Вот пример реализации ls: http://www.boost.org/doc/libs/1_43_0/libs/filesystem/example/simple_ls.cpp

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

-1
ответ дан 4 December 2019 в 06:29
поделиться

Возможно, это не самое простое решение, но как насчет того, чтобы сделать что-то эквивалентное StringBuilder в C #?

Создайте list > , тогда вы сможете добавьте в список все векторы, полученные в результате вызовов scanDir () .

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

В качестве альтернативы вы можете создать новый класс (при необходимости, производный от vector ) и внутренне использовать список > для хранения элементов. Затем вы просто заставляете свои итераторы перебирать элементы в первом списке, а затем, когда он достигает конца, переходите к элементам в следующем списке, возвращая container :: end только тогда, когда вы достигли конца последнего списка.

-1
ответ дан 4 December 2019 в 06:29
поделиться

Как насчет вспомогательной функции?

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;
}
0
ответ дан 4 December 2019 в 06:29
поделиться
vector<string> fileList;
vector<string> temp( scanDir(subdirname) );

fileList.insert(fileList.end(), temp.begin(), temp.end());

Надеюсь, это вам помогло.

3
ответ дан 4 December 2019 в 06:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: