C ++将一个向量追加到另一个向量

时间:2010-07-20 19:20:05

标签: c++ stl vector append

我完全理解这个问题已经被问了很多,但是我要求一个特定的变体,我的搜索foo已经放弃,因为我只找到附加一个的算法矢量到另一个,但没有一个从函数返回。

我有这个功能列出目录中的所有文件:

vector<string> scanDir( const string& dir )

可以在内部调用自己(对于子目录)。

我需要 short 方法将返回的值附加到调用者的向量中。我脑子里有这样的东西(当然它不存在:():

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

我担心存储返回值并将其插入fileList会带来性能不佳。我的意思是:

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

谢谢!

PS:我并没有强迫自己使用vector,任何其他同样表现良好的容器并且可以阻止潜在的大型复制操作。我很好。

10 个答案:

答案 0 :(得分:15)

为什么不将矢量作为参数传递?然后每个调用都可以附加到同一个向量,而无需复制。或者创建一个实现类,将元素累积到成员对象中。

答案 1 :(得分:9)

如果您有能力更改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()));

编辑(见评论......)

对于使用此函数进行类成员初始化,您可以在构造函数中调用它,如在

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)) { }
}

我更喜欢第一个版本的性能(和其他)原因......

答案 2 :(得分:8)

  

PS:我并没有强迫自己使用vector,任何其他同样表现良好的容器并且可以阻止潜在的大型复制操作。我很好。

好吧,如果您使用list并致电a.splice(a.end(), b);,您将完全避免复制操作。 list通常是链接列表而不是数组,就像vector一样,因此这会带来很多性能和使用含义。但是拼接在O(1)中运行,所以这是一个很好的好处。

答案 3 :(得分:3)

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

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

我希望能帮助你。

答案 4 :(得分:2)

使用std :: list并使用std :: list :: splice附加。

来自the docs for splice

  

该操作不涉及构造或销毁任何元素对象,除第三个版本外,它是在恒定时间内执行的。

答案 5 :(得分:1)

而不是

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

你可以做到

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

继续复制:

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

答案 6 :(得分:0)

递归函数必须多次复制所有内容,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 
}

答案 7 :(得分:0)

辅助函数怎么样?

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;
}

答案 8 :(得分:-1)

这可能不是最简单的解决方案,但是做一些与C#的StringBuilder等效的事情呢?

点击list<vector<string> >,然后您可以将通过调用scanDir()获得的所有向量附加到列表中。

如果你最后必须有一个向量,那么你可以在创建一个新向量后,将它分配得足够大,这样它就不需要调整大小,并组装你的成品。

或者,您可以创建一个新类(如果需要,它派生自vector&lt; T&gt;)并在内部使用列表&lt; vector&lt; T&gt; &GT;存储元素。然后你只需要让你的迭代器遍历第一个列表中的元素,然后当它到达结尾时去下一个列表中的元素,只有当你到达最后一个列表的末尾时才返回container :: end。

答案 9 :(得分:-1)

我知道这并没有直接回答你的问题,但就你的基本目标而言,你可能只想简单地在boost :: filesystem方面重新实现你的功能。目录迭代器已经递归,因此您不需要进行自己的递归调用。您可以简单地在迭代器上的循环中填充列表。有一个ls的示例实现: http://www.boost.org/doc/libs/1_43_0/libs/filesystem/example/simple_ls.cpp

您还可以获得(理论上)平台独立性的更多好处,相对广泛的采用(通过更多采用可以更快地发现错误)等等