Ссылка на std :: array разных размеров вместо std :: array в базовом классе - PullRequest
3 голосов
/ 29 мая 2011

У меня следующая проблема. Рассмотрим классы

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 :: vector на std :: array соответствующего размера, чтобы сохранить накладные расходы, вызванные тремя дополнительными указателями в std :: vector. (Это потому, что у меня будут миллионы граней ...) Теперь есть ли возможность иметь общую функцию доступа к лицу с помощью std :: array, как с std :: vector выше? Со стандартным C-массивом я просто вернул бы указатель на первую запись массива и его размер. Есть ли STL-способ сделать то же самое или нечто подобное? Или есть какой-нибудь другой хороший способ достижения этой функциональности?

Спасибо за чтение и, возможно, за ответ !! Andreas

Ответы [ 2 ]

6 голосов
/ 29 мая 2011

std::arrays разных размеров разных типов, поэтому я не вижу способа сделать то, что вы хотите.Но что, если вы вернете константы begin() и end() const в любой контейнер, который у вас внутри, или в объект малого диапазона, содержащий оба?Таким образом вы отделяете размер контейнера от интерфейса, оставляя его для реализации.

EDIT : просто чтобы уточнить, чтобы скрыть представление хранилища данных (в данном случае,размер 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;

};

1 голос
/ 29 мая 2011

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(), и вы добавляете больше элементов, чем его емкость.Изменение размера не происходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...