【问题标题】:What c++11 paradigm should I use to minimize memory-usage and minimize copying?我应该使用什么 c++11 范例来最小化内存使用和最小化复制?
【发布时间】:2014-03-25 06:45:57
【问题描述】:

问题

我有一个抽象接口Series 和一个具体类Primary_Series,它通过存储大量std::vector<> 值来满足接口。

我还有另一个具体的类Derived_Series,它本质上是Primary_Series 的变换(例如一些大的Primary_Series 乘以3),我想节省空间,所以我不想存储作为成员的整个派生系列。

template<typename T>
struct Series
{
  virtual std::vector<T> const& ref() const = 0;
};

template<typename T>
class Primary_Series : Series<T>
{
  std::vector<T>  m_data;
public:
  virtual std::vector<T> const& ref() const override { return m_data; }
}

template<typename T>
class Derived_Series : Series<T>
{
  // how to implement ref() ?
}

问题

我应该如何改变这个接口/纯虚拟方法?

我不想按值返回该向量,因为它会为Primary_Series 引入不必要的复制,但在Derived_Series 的情况下,我肯定需要创建某种临时向量。但是后来我面临的问题是,一旦调用者完成了该向量,如何使该向量消失。

如果ref() 会返回一个临时引用,该引用会随着引用的消失而消失,那就太好了。

这是否意味着我应该使用某种std::weak_ptr&lt;&gt;?这是否符合Primary_Series 的工作方式?

什么是满足“最小化内存使用”和“最小化复制”要求的最佳方法,包括在调用者完成后使 Derived_Series 临时消失?

【问题讨论】:

  • derived_series 是否意味着某种对 primary_series 的惰性转换?如果是这样,您是否想让程序员负责保持引用不悬空?如果是这样,我建议参考或 ptr。但是我会推荐在derived_seriesprimary_series 中的shared_ptr,它指向数据。然后你需要解决转换数据的问题......所以我认为你不能正确(即有效和懒惰地)实现给定的接口。您是否有理由要使用此接口而不是实现...
  • 下标 [] 运算符、at() 函数或序列上的迭代器?
  • @Xarn 是的,懒惰的转换正是我想要的,但是有没有办法让derived_series 在调用者完成后消失?我刚刚看到您的第二条评论 - 这可能会更好,因为它更好地隐藏了实现,并将清理的责任完全转移给了调用者。如果您将其发布为答案,我想我可以接受它作为答案。
  • 好吧,我会用少一点“漫无边际”的风格来写它,并且会详细说明一点......
  • @Xarn 我刚刚意识到使用下标运算符或使用 at(),在 Primary_Series 的情况下,我最终会制作一个完整的副本,所以我实际上回到了 return-by -value,除非我没有正确理解您的意思。

标签: c++ memory c++11 interface ownership


【解决方案1】:

好吧,界面设计本身就有点问题,因为 C++ 并没有真正偷懒。

现在,由于 Derived_Series 应该是原始 Primary_Series 的惰性求值(因为您希望节省空间)变换,因此您不能返回完整的胖向量的引用。 (因为这需要你先构建它。)

所以我们必须改变_Series共享数据的接口和方式。使用std::shared_ptr&lt;std::vector&lt;&gt;&gt;Primary_SeriesDerived_Series 之间共享数据,这样Primary_Series 超出范围就不会使您的转换数据无效。

然后您可以将界面更改为更“类似矢量”。也就是说,实现一些(或全部)常用数据访问函数(operator[]at()...)和/或自定义迭代器,它们从原始系列返回转换后的值。这些将让您隐藏一些实现细节(转换的惰性、数据共享......)并且仍然能够以最大的效率返回转换后的值,并让人们将您的类用作“类向量”,因此您不必改变你的设计。 (~任何使用向量的算法都可以在意识到你的类后使用它。)

I've also sketched out a very basic example of what I mean.

(注意:如果您有一个多线程设计和可变的Primary_Series,您将不得不考虑一下您需要同步的位置和内容。)

---编辑---
在仔细考虑之后,我还必须注意Derived_Series 的实现无论如何都会有点痛苦。它的方法必须按值返回,它的迭代器基本上是伪装成更高类迭代器的输入迭代器,因为延迟评估值的引用返回实际上不起作用,或者它必须填充它自己的数据结构,如评估原始系列的位置,这将带来完全不同的权衡。

【讨论】:

  • +1 我喜欢你对设计的看法。对于Derived_Series 的简单情况,乘数是另一个Series 的某个实例的3 倍,一些类似向量的实现实际上会是什么样子?您能否在答案中添加一些代码以进行澄清?谢谢。
  • @kfmfe04 我会尝试整理一些示例代码,但请给我一些时间... ;-) 最近我一直在做的唯一编码是使用旧的 GCC连lambdas、函数之类的都没听说过,所以我需要再弄清楚。
  • @kfmfe04 查看答案中的链接以获取一个非常基本的示例。
【解决方案2】:

一种方法是定义您自己的iterator 并将您的vector&lt;T&gt; 设为私有。基本上,您将拥有begin()end() 的纯虚拟访问器。 Derived_Series 只会包装 Primary_Series 的迭代器并即时转换值。

【讨论】:

    【解决方案3】:

    一种解决方案是使用std::shared_ptr&lt;vector&lt;T&gt; &gt; 将向量存储在您的基类中,并使用它来返回向量的值。基类只返回其成员值,派生类创建一个新向量并通过 shared_ptr 返回它。那么当调用者不再需要派生类的返回值时,它会被自动销毁。

    或者,您可以设计您的类以模仿std::vector&lt;T&gt; 的接口,但设计基类使其返回转换后的值而不是常规值。这样一来,就不需要返回了。如果您不想为std::vector&lt;T&gt; 具有的所有函数编写方法,您可以制作某种可以迭代和转换std::vector&lt;T&gt; 的转换迭代器。那么你甚至不必有一个复杂的类层次结构。

    【讨论】:

    • 那么你的意思是在Primary_Series 的情况下,有两个shared_ptr - 一个作为成员,一个由函数调用返回(当然,两者都指向同一个向量),而在Derived_Series案例,函数调用返回的只有一个?我认为这可能行得通。
    猜你喜欢
    • 2011-04-03
    • 1970-01-01
    • 2017-03-12
    • 2012-11-16
    • 2021-10-25
    • 2014-07-09
    • 2020-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多