【问题标题】:How to return large vector from class without copying data如何在不复制数据的情况下从类中返回大向量
【发布时间】:2016-09-11 18:12:34
【问题描述】:

我正在编写一个程序,其中一个类的数据成员很大 std::vector(大约 100k - 1M 项)。其他类需要能够访问此向量。目前我有一个返回向量的标准访问器函数,但这会返回我相信的向量的副本。我认为只返回一个迭代器或指向第一个元素的指针会更节省内存和时间。但是,如果我这样做,那么如何使用此指针来运行 vector 并知道何时停止(即向量在哪里结束)?

我的代码如下所示:

class MyClass
{
    private:
        std::vector<MyObj> objects_;
        //...

    public:
        std::vector<MyObj> getObjects() { return objects_; }
        //...
}

当我想遍历(模拟的)连接向量时,这个问题以另一种形式出现。如果我有MyClassvector,我希望能够遍历所有包含的object_ 向量。我从this answer 知道 boost::join 符合我的想法,但我认为我需要返回副本才能使其正常工作。我可以返回一个指向向量的指针,并且仍然保留对它和其他连续迭代的能力吗?

【问题讨论】:

    标签: c++ c++11 vector


    【解决方案1】:

    为避免性能损失,请返回引用。

    // Non-const version
    std::vector<MyObj>& getObjects() { return objects_;}
    
    // const version
    std::vector<MyObj> const& getObjects() const { return objects_; }
    

    但是,在进行更改之前,您必须考虑将引用公开给成员变量的不利之处。它使您的课程不那么灵活。你不能轻易地将objects_ 更改为不同类型的容器,如果这样做更有意义而不影响类的所有用户。

    【讨论】:

    • 您能否进一步解释一下为什么公开引用会使更改容器类型变得更加困难?你是说模板类型吗?
    • @marcman,如果你的容器需要是一个集合而不是一个列表,你不能轻易地将objects_ 更改为std::set&lt;MyObj&gt; 类型。这会破坏所有依赖于objects_ 的客户端为std::vector&lt;MyObj&gt; 类型。
    • @marcman 在我的回答中查看容器更改的解决方案
    【解决方案2】:

    通过委派给vector 数据成员,让您的类充当一个集合。 当然,您可能需要重新访问使用MyClass 的代码 但是,在 getObjects() 被注释掉的情况下,编译器会告诉你 + 大多数更改可能

    MyClass heapsOfThem;
    // ...
    // just delete the `getObjects()` *and use MyClass::iterator*
    // instead of std::vector::iterator.
    // for(std::vector<MyObj>::iterator it=
    //    heapsOfThem.getObjects().begin()...
    // )
    for(MyClass::iterator it=heapsOfThem.begin()...)
    

    委托代码位于以下行 - 一旦您修复了调用代码,您就可以改变主意将哪种类型(向量、列表、集合)用作对象的内部容器,而无需更改调用代码。

    class MyClass
    {
        private:
            std::vector<MyObj> objects_;
            //...
    
        public:
    
    
            const size_t size() const {
              return objects_,size();
            }
            MyObj& operator[](size_t i) {
              return objects_[i];
            }
            const MyObj& operator[](size_t i) const {
              return objects_[i];
            }
    
            using iterator = std::vector<MyObj>::iterator;
            iterator begin() {
              return objects_.begin();
            }
            iterator end() {
              return objects_.end();
            }
            // TODO const iterators following the same pattern
    
            // *if you aren't good enough with the above*
            // uncomment it and let it return a *reference* 
            // std::vector<MyObj>& getObjects() { return objects_; }
            //...
    }
    

    【讨论】:

    • 这并不能真正避免更改集合类型的问题——调用代码对std::vector&lt;MyObj&gt;::iterator具有相同的依赖关系。
    • @M.M “这并不能真正避免问题” 是的,确实如此。感谢您表明需要额外的解释,我添加了它
    • 即使在返回vector的情况下,调用代码仍然可以避免容器发生任何源更改。
    • @M.M - “当容器发生变化时,调用代码仍然可以避免任何源代码变化,” - 当然可以。
    • 是的,那么返回迭代器而不是向量有什么好处?
    【解决方案3】:

    您可以重构该类,使其具有返回数组元素和数组大小的公共方法,因此所有其他类都可以获取值,而无需复制整个向量。

    public:
        unsigned int getMyObjArraySize();
        MyObj getMyObjElementAt(unsigned int index);
    

    使用这种方法,向量只有一个实例,但任何协作都可以通过两个公共方法完成,这两个公共方法通过索引公开大小和访问值。

    这种方法适合使用 for 循环而不是迭代器。

    MyClass myClass;
    // ...
    MyObj myObj;
    
    for(unsigned int i; i < myClass.getMyObjArraySize(); i++) {
        myObj = myClass.getMyObjElementAt(i);
        // do stuff
    }
    

    【讨论】:

    • 这个问题最终是我需要遍历整个向量,并且我想在不复制所有数据的情况下这样做。您的 getMyObjectElementAt 返回每​​个元素的副本,因此最终它与返回整个向量并遍历它没有什么不同
    【解决方案4】:

    返回指向向量的指针没有问题。

    std::vector<MyObj>* getObjects() { return &objects_; }
    

    然后当想要迭代它时,只需取消引用:

    std::vector<MyObj>* objectsPtr = getObjects();
    for (auto& it : *objectsPtr)
    {
       ...
    }
    

    但是,请确保在读取向量时没有写入向量,因为这会使迭代器无效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-04
      • 1970-01-01
      • 2017-07-12
      • 1970-01-01
      • 2017-04-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多