我应该使用哪种 c++11 范例来最小化内存使用量并最小化复制?

What c++11 paradigm should I use to minimize memory-usage and minimize copying?

本文关键字:最小化 内存 使用量 复制 范例 c++11 我应该      更新时间:2023-10-16

PROBLEM

我有一个抽象接口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<>? 这是否符合Primary_Series的工作方式?

满足"最小化内存使用量"和"最小化复制"要求(包括在调用方完成后使Derived_Series暂时消失)的最佳方法是什么?

好吧,界面设计本身带来了一些问题,因为C++并没有真正懒惰。

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

因此,我们必须更改_Series共享数据的界面和方式。使用std::shared_ptr<std::vector<>>Primary_SeriesDerived_Series之间共享数据,以便超出范围Primary_Series不会使转换的数据无效。

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

我还勾勒出了一个非常基本的例子来说明我的意思。

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

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

一种解决方案是使用std::shared_ptr<vector<T> >将向量存储在基类中,并使用它来返回向量的值。基类只返回其成员值,派生类创建一个新向量并通过shared_ptr返回该向量。然后,当调用方不再需要派生类的返回值时,它将自动销毁。

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

一种方法是定义你自己的iterator,并使你的vector<T>私有。基本上,您将拥有纯虚拟访问器begin()end().Derived_Series将包装Primary_Series的迭代器并动态转换值。