【问题标题】:Passing a vector of unique_ptr to an object. the vector becomes a member variable. correct approach?将 unique_ptr 的向量传递给对象。向量成为成员变量。正确的方法?
【发布时间】:2015-01-04 05:22:22
【问题描述】:

我将称为“所有者”的一个对象在其生命周期内具有数据对象向量的明确所有权。

这些存储为 unique_ptr 的向量。

一个对象/类,称为“Output”,需要以多种不同的方法查看这些数据对象,因此某种的引用/指针/变量是“Output”的成员变量.

输出在其构造函数中接收数据对象的向量。

我想了三种方法来实现这一点。什么被认为是最好的方法?

选项 1 - “输出”对象将数据 vec 存储为 const 引用:

class Output {
    // output wants the data:
 public:
    Output(std::vector<std::unique_ptr<Data>> const & in)
        : my_lot_of_data(in) {
    };
    std::vector<std::unique_ptr<Data>> const & my_lot_of_data;
}

由“所有者”实例化:

 data_vec_.push_back(std::unique_ptr<Data>(new Data));
 /* stuff happens to data */
 Output output(data_vec_);

选项 2 - “输出”对象将数据 vec 存储为 const 指针:

class Output {
    // output wants the data:
 public:
    Output(std::vector<std::unique_ptr<Data>> const * in)
        : my_lot_of_data(in) {
    };
    std::vector<std::unique_ptr<Data>> const * my_lot_of_data;
}

由“所有者”实例化:

 data_vec_.push_back(std::unique_ptr<Data>(new Data));
 /* stuff happens to data */
 Output output(&data_vec_);

选项 3 - “输出”对象接收原始指针:

class Output {
    // output wants the data:
 public:
    Output(std::vector<Data*> in)
        : my_lot_of_data(in) {
    };
    std::vector<Data*> const my_lot_of_data;
};

由“所有者”实例化:

 data_vec_.push_back(std::unique_ptr<Data>(new Data));
 /* stuff happens to data */
 std::vector<Data*> data_as_raw;
 data_as_raw.resize(data_vec_.size());
 std::transform(data_vec_.begin(), data_vec_.end(), data_as_raw.begin(), [](std::unique_ptr<Data> const & x) {return x.get();});
 Output output(data_as_raw);

其他查询: 在选项 1 和 2 中是否清楚输出没有数据的所有权,即使它存储为 unique_ptrs? 选项 3 在呼叫现场是否混乱?需要多 3 行才能达到相同的结果。

这里的最佳做法是什么?

【问题讨论】:

  • 如有疑问,请使用 shared_ptr
  • 我个人喜欢const&amp;,不要使用std::transform。但这可能取决于Output 对数据的处理方式。
  • @Barry 在我的现实生活中,Output 只是读取一些值,然后创建各种输出文件。不修改数据。
  • @NeilKirk 问题是我一直在阅读 shared_ptr 不应该在没有终身共享时使用。我知道调用“输出”对象的对象是整个程序生命周期的所有者。
  • @NeilKirk:强烈反对。过度使用 shared_ptr 是一种瘟疫,应该避免。如有疑问,请使用 unique_ptr。

标签: c++ oop c++11


【解决方案1】:

如果您只需要“读取一些值,然后 [创建] 各种输出文件”,我只会将其设为由 const-ref 获取的函数:

void output(std::vector<std::unique_ptr<Data>> const& data) {
    // stuff
}

我更喜欢 const&amp;const* 的用法语义(data[0] vs (*data)[0]),并且绝对更喜欢两者都传递原始数据 - 不要放弃你的明确所有权(在这个考虑到构建vector&lt;Data*&gt; 很烦人,这甚至不是为了方便)

【讨论】:

  • 我的问题是输出类有几个函数都使用相同的数据。因此我想把它作为一个成员变量,这样很明显,每组输出的所有数据都是相同的,这样我就不必在每个函数中都将数据作为参数。您关于 const* 语义和创建原始数据的难度的观点对我来说似乎非常明智。
【解决方案2】:

由于您使用的是 unique_ptr,因此您不打算与任何可能持续时间超过 Owner 的对象共享此数据,因此简单的 const 引用应该是好的。我会推荐一个不错的 typedef:

typedef std::vector<std::unique_ptr<Data>> OwnerDataSet;
Output(const OwnerDataSet &in)

方法一的优点是简单明了。其他人只是无缘无故地将其复杂化。

unique_ptr的作用是在std::vector被破坏时删除新的Data。这里的替代方法是复制 Data 的一个实例,而不是调用 new。如果您不需要使用指针,则不需要像 unique_ptr 这样的特殊处理来保证它们的安全。

typedef std::vector<Data> OwnerDataSet;
OwnerDataSet results;
Data match = findoneresult();
results.push_back(match); // copy something from Owner
Output (results);

更进一步,从您的示例中不清楚为什么要在 Output 类之外维护 std::vector 。由于您在传递的所有内容上都调用 std::unique_ptr(new T) ,我怀疑您仅将它与输出一起使用,因此您可以这样做:

class Output : public std::vector<Data> {
    void PrintToScreen();
    void WriteToDatabase();
    void OrWhatever();
};

Output x;
Data match = findoneresult();
x.push_back(findoneresult());
x.PrintToScreen();

【讨论】:

  • 我认为 typedef 在多个类中定义相同类型时是必要的,但在我发布的最后一个代码示例中它消失了,因为向量只在一个地方定义。
  • 从那以后我了解到不建议从 std 容器继承(因为它们没有虚拟析构函数,因此使用 new/delete 会带来风险),因此类 Output 应该将向量作为成员(或使用私有继承)。当然,我发布的静态范围示例没有任何风险。
【解决方案3】:

我会混合前两种方法:让 Output 构造函数通过 const-ref 获取向量,但将其存储为 const 指针。这是因为 const 指针可以被复制,因此整个 Output 对象可以在需要时进行赋值。

【讨论】:

    【解决方案4】:

    如果可能的话,我会完全避免传递向量,并将 unique_ptr 的向量封装在 Owner 中。 Output 是否有指向 Owner 的指针/引用并从 Owner 获取指向单个 Data 元素的原始指针?

    class Owner {
      private:
        std::vector<std::unique_ptr<Data>> data_vec_;
      public:
        const Data* getData(size_t i) const { return data_vec_.at(i).get(); }
        size_t getSize() const { return data_vec_.size(); }
    };
    
    class Output {
      private:
        const Owner& owner_;   
      public:
        Output(const Owner& owner) : owner_(owner) { }
        void doSomething() { 
             // get some data
             auto data = owner_.getData(0);
    
             // use data...
        }
    };
    

    【讨论】:

    • 为什么设计这么复杂?
    • @Walter 首先,我不认为它过于复杂。传递Owner 比传递std::vector&lt;std::unique_ptr&lt;Data&gt;&gt; 简单得多。其次,这意味着您可以轻松更改数据在未来的存储方式,例如存储到不同的容器,或使用 shared_ptr。
    • 这会给单元测试带来问题。如果 Output 只采用众所周知的数组类型,则测试比必须检测以后由于 Owner.getData() 内部更改而出现的任何错误要容易
    • @KennyOstrom 如果你想让单元测试更容易,你可以很容易地传递一个接口而不是具体的Owner
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 2020-09-06
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多