【问题标题】:Reference to std::array of different sizes instead of std::array in a base class在基类中引用不同大小的 std::array 而不是 std::array
【发布时间】: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 的解决方案。

标签: c++ arrays vector reference


【解决方案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 的诱导开销。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-17
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
  • 2018-06-29
  • 1970-01-01
  • 2021-04-23
相关资源
最近更新 更多