代码之家  ›  专栏  ›  技术社区  ›  rubenvb

C++将一个向量附加到另一个向量上。

  •  9
  • rubenvb  · 技术社区  · 14 年前

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

    我有一个列出目录中所有文件的函数:

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

    谢谢!

    10 回复  |  直到 14 年前
        1
  •  10
  •   MartinStettner    14 年前

    如果你能改变 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
  •  15
  •   Philipp    14 年前

        3
  •  8
  •   Philipp    14 年前

    PS:我并不是强迫自己使用vector,任何其他的容器都可以很好的执行,并且可以防止潜在的大拷贝操作。

    list 打电话给我 a.splice(a.end(), b); 列表 通常是一个链表,而不是数组 vector ,因此这对性能和使用有很大的影响。但是拼接在O(1)中运行,所以这是一个很好的好处。

        4
  •  3
  •   virious    14 年前
    vector<string> fileList;
    vector<string> temp( scanDir(subdirname) );
    
    fileList.insert(fileList.end(), temp.begin(), temp.end());
    

        5
  •  2
  •   Philipp    14 年前

    the docs for splice

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

        6
  •  1
  •   Alexandre C.    14 年前

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

    你能做到的

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

    然后继续复制:

    fileList.insert(fileList.end(), temp.begin(), temp.end());
    
        7
  •  0
  •   sukru    14 年前

    递归函数必须多次复制所有内容,精确地说是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 
    }
    
        8
  •  0
  •   David Gladfelter    14 年前

    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;
    }
    
        9
  •  -1
  •   Andrew Shelansky    14 年前

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

    制造 list<vector<string> > scanDir()

    如果你必须在最后有一个单独的向量,你可以,一旦做了一个新的向量,分配它足够大,这样它就不需要调整大小,然后组装你的成品。

    或者,您也可以创建一个新类(如果需要,它派生自vector<T>)并在内部使用列表<向量<T>&燃气轮机;存储元素。然后,您只需让迭代器遍历第一个列表中的元素,然后当它到达末尾时,转到下一个列表中的元素,当您到达最后一个列表的末尾时,只返回container::end。

        10
  •  -1
  •   frankc    14 年前

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

    您还可以从(理论上)平台独立性、相对广泛的采用(随着采用率的提高,bug被更快地发现)等方面获得额外的好处