问题
我有一个抽象接口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
临时消失?
答案 0 :(得分:3)
接口设计很好,因为它带来了一些问题,因为C ++并不是真的很懒。
现在,由于Derived_Series
应该是一个懒惰评估(因为你想节省空间)原始Primary_Series
的变换,你不能返回一个完整的胖矢量的引用。 (因为这需要你先构建它。)
因此我们必须更改界面以及_Series
共享数据的方式。使用std::shared_ptr<std::vector<>>
在Primary_Series
和Derived_Series
之间共享数据,以便超出范围的Primary_Series
无法使转换数据无效。
然后,您可以将界面更改为“类似矢量”。也就是说,实现一些(或所有)通常的数据访问函数(operator[]
,at()
...)和/或自定义迭代器,它们从原始系列返回转换后的值。这些将让你隐藏一些实现细节(变换的懒惰,数据的共享......),并且仍然能够以最大的效率返回变换的值,并让人们将你的类用作“矢量样”,所以你不必改变你的设计。 (〜任何使用过矢量的算法都可以在知道之后使用你的类。)
<强> I've also sketched out a very basic example of what I mean. 强>
(注意:如果你有多线程设计和可变Primary_Series
,你将不得不考虑一下你需要同步的地点和位置。)
--- ---编辑
在仔细考虑之后,我还要注意,Derived_Series
的实现无论如何都会有点痛苦。它的方法必须按值返回,它的迭代器基本上是伪装成更高类迭代器的输入迭代器,因为对于延迟计算值的引用返回并不真正起作用,或者它必须填写它自己的数据结构,如评估原始系列的位置,这将带来完全不同的权衡集。
答案 1 :(得分:1)
一种解决方案是使用std::shared_ptr<vector<T> >
将向量存储在基类中,并使用它来返回向量的值。基类只返回其成员值,派生类创建一个新的向量并通过shared_ptr返回。然后,当调用者不再需要为派生类返回的值时,它将被自动销毁。
或者,您可以将类设计为模仿std::vector<T>
的接口,但设计基类以便返回转换后的值而不是常规值。这样,就不需要任何回报。如果你不想为std::vector<T>
所有的函数编写方法,你可以做一些可以迭代并转换std::vector<T>
的转换迭代器。那么你甚至不必拥有复杂的类层次结构。
答案 2 :(得分:1)
一种方法是定义自己的iterator
并将vector<T>
设为私有。基本上,您将拥有begin()
和end()
的纯虚拟访问者。而Derived_Series
将只包装Primary_Series
的迭代器并动态转换值。