在基类中引用不同大小的 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&amp; operator[](unsigned idx);,以及另一个用于获取它们的计数的函数。 @Nawaz:std::vector 的实现(至少对于 gcc 而言)除了存储的数据本身之外,还使用三个指针来表示存储的开始、存储的结束和存储的结束。如果一个人有大量的 std::vectors 而条目相对较少,这将成为不可忽视的开销。 @Timo:这是我一直在考虑的解决方案,但它似乎不是一个“好的”类似 STL 的解决方案。 【参考方案1】:

std::arrays 的不同大小是不同的类型,所以我看不出有什么方法可以做你想做的事。但是,如果您将begin()end() const 迭代器返回到您内部保存的任何容器,或者包含两者的小范围对象,该怎么办?这样您就可以将容器的大小与接口分离,将其留给实现。

编辑:澄清一下,为了隐藏数据存储表示(在本例中为std::array 的大小),您需要自己的face 迭代器类。这在指针方面很容易实现,因为对于每个face 特化,您都知道底层数据结构的大小、开始和结束。但显然不能直接在界面中使用std::arraybegin()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; ;
;

但这会导致trianglequadrilateral 具有不同的基类:face&lt;3&gt;face&lt;4&gt; 是两个不同的类。这意味着,您不能将trianglequadrilateral 混合在一起,例如在标准容器中,并且您不能使用相同基类类型的指针访问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的主要内容,如果未能解决你的问题,请参考以下文章

不能在基类中声明 Signal

mypy:基类没有属性x,如何在基类中键入提示

如何在基类中实例化泛型类型?

在基类中访问 QPainter

在基类和派生类中使用基类的装饰器

在基类中存储指向派生类函数的指针