C ++ добавляет один вектор в другой - PullRequest
9 голосов
/ 20 июля 2010

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

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

vector<string> scanDir( const string& dir )

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

Мне нужен короткий способ добавления возвращаемого значения к вектору вызывающей стороны. Я имею в виду что-то вроде этого (но, конечно, это не существует :():

vector<string> fileList;
//...
fileList.append( scanDir(subdirname) );

Боюсь, что сохранение возвращаемого значения и его вставка в fileList приведет к снижению производительности. Я имею в виду следующее:

vector<string> temp( scanDir(subdirname) );
copy( temp.begin(), temp.end(), back_inserter(fileList) );

Спасибо!

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

Ответы [ 10 ]

15 голосов
/ 20 июля 2010

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

10 голосов
/ 20 июля 2010

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

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

8 голосов
/ 20 июля 2010

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

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

3 голосов
/ 20 июля 2010
vector<string> fileList;
vector<string> temp( scanDir(subdirname) );

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

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

2 голосов
/ 20 июля 2010

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

С документы для сращивания :

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

1 голос
/ 20 июля 2010

вместо

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

вы можете сделать

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

и продолжить с копией:

fileList.insert(fileList.end(), temp.begin(), temp.end());
0 голосов
/ 21 июля 2010

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

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

0 голосов
/ 21 июля 2010

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

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

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

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

0 голосов
/ 20 июля 2010

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

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 голосов
/ 20 июля 2010

Рекурсивная функция должна будет копировать все несколько раз, 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 
}
...