在基类中引用不同大小的 std::array 而不是 std::array
Posted
技术标签:
【中文标题】在基类中引用不同大小的 std::array 而不是 std::array【英文标题】:Reference to std::array of different sizes instead of std::array in a base class 【发布时间】:2011-05-29 08:13:07 【问题描述】:我有以下问题。考虑类
class face
virtual std::vector<ptr>& get_vertices(void) const = 0;
;
class triangle : public face
private:
std::vector<ptr> vertices;
public:
std::vector<ptr>& get_vertices(void) const return vertices; ;
;
class quadrilateral : public face
private:
std::vector<ptr> vertices;
public:
std::vector<ptr>& get_vertices(void) const return vertices; ;
;
显然,三角形和四边形总是分别有 3 个和 4 个顶点。 因此,我想用适当大小的 std::array 交换 std::vector,以节省 std::vector 中的三个附加指针引起的开销。 (这是因为我将拥有数百万张脸......)现在,是否有机会在 std::array 和上面的 std::vector 中拥有一个共同的访问函数? 使用标准 C 数组,我只需返回一个指向第一个数组条目及其大小的指针。有没有一种 STL 方式来做同样的事情或类似的事情?或者有没有其他好的方法来实现这个功能?
感谢您阅读并可能回答! 安德烈亚斯
【问题讨论】:
你在说哪个additional pointers in std::vector
?
您真的需要将它们作为 STL 容器返回吗?如果只有 3 或 4 个元素,我认为向量或数组没有太大优势。我只是将指针存储在一个固定大小的数组中,并有一些直接返回元素的函数或运算符,如virtual ptr& operator[](unsigned idx);
,以及另一个用于获取它们的计数的函数。
@Nawaz:std::vector 的实现(至少对于 gcc 而言)除了存储的数据本身之外,还使用三个指针来表示存储的开始、存储的结束和存储的结束。如果一个人有大量的 std::vectors 而条目相对较少,这将成为不可忽视的开销。
@Timo:这是我一直在考虑的解决方案,但它似乎不是一个“好的”类似 STL 的解决方案。
【参考方案1】:
std::arrays
的不同大小是不同的类型,所以我看不出有什么方法可以做你想做的事。但是,如果您将begin()
和end()
const 迭代器返回到您内部保存的任何容器,或者包含两者的小范围对象,该怎么办?这样您就可以将容器的大小与接口分离,将其留给实现。
编辑:澄清一下,为了隐藏数据存储表示(在本例中为std::array
的大小),您需要自己的face
迭代器类。这在指针方面很容易实现,因为对于每个face
特化,您都知道底层数据结构的大小、开始和结束。但显然不能直接在界面中使用std::array
的begin()
和end()
。
例子:
这是一个快速而肮脏的示例,说明如何使用指针实现部分前向迭代器行为。我使用虚拟基类和 OP 的实现之一作为起点。我还省略了所有构造函数、赋值运算符等。它还假设一个类 Edge(大概是 2D 或 3D 点?)。
class face
public:
typedef Edge* iterator;
typedef const Edge* const_iterator;
virtual iterator begin() = 0;
virtual const_iterator begin() const = 0;
virtual iterator end() = 0;
virtual const_iterator end() const = 0;
virtual size_t size() const = 0;
virtual ~face() ;
;
class triangle : public virtual face
public :
virtual iterator begin() return m_edges.begin();
virtual const_iterator begin() const return m_edges.begin();
virtual iterator end() return m_edges.end();
virtual const_iterator end() const return m_edges.end();
virtual size_t size() const return m_edges.size();
private:
std::array<Edge, 3> m_edges;
;
【讨论】:
这似乎是一个合适的解决方案。但是,我认为可能有一个“更好”的解决方案,即已经实现了一些与数组大小无关的迭代器。无论如何,非常感谢! @Andreas,我添加了一个简单的例子,只是为了给出一个简单实现的想法。请谨慎对待,做的太仓促了!【参考方案2】:std::array
在这里似乎不是一个解决方案,因为get_vertices
是一个纯虚函数,这意味着您可能希望使用基类类型的指针(或引用)来访问它。如果你使用std::array
,你必须提供一个整数值作为std::array
类模板的第二个参数,如果你把face
作为一个类模板,这是可能的:
template<size_t N>
class face
virtual std::array<ptr, N>& get_vertices(void) const = 0;
;
class triangle : public face<3>
//...
std::array<ptr, 3>& get_vertices(void) const return vertices; ;
;
class quadrilateral : public face<4>
//...
std::array<ptr, 4>& get_vertices(void) const return vertices; ;
;
但这会导致triangle
和quadrilateral
具有不同的基类:face<3>
和face<4>
是两个不同的类。这意味着,您不能将triangle
和quadrilateral
混合在一起,例如在标准容器中,并且您不能使用相同基类类型的指针访问get_vertices
,因为现在没有单个基类。每个派生类都有自己的基类。
所以解决办法是这样的:
class triangle : public face
private:
std::vector<ptr> vertices;
public:
triangle()
//it ensures that you've a vector of size 3, no more no less
vertices.reserve(3);
std::vector<ptr>& get_vertices(void) const return vertices; ;
;
同样,您可以在quadrilateral
:
quadrilateral()
//it ensures that you've a vector of size 4, no more no less
vertices.reserve(4);
现在,您的向量不会随意将其自身调整到比实际需要更大的大小,因为您已经通过调用 reserve()
定义了容量,并且您将添加比其容量更多的项目。不会调整大小。
【讨论】:
正如我在上面的评论中所说,这只会让我免于“昂贵”的重新分配,但不能免于 std::vector 的诱导开销。以上是关于在基类中引用不同大小的 std::array 而不是 std::array的主要内容,如果未能解决你的问题,请参考以下文章