#c #arrays #vector #reference
#c #массивы #вектор #ссылка
Вопрос:
У меня следующая проблема. Рассмотрим классы
class face {
virtual std::vector<ptr>amp; get_vertices(void) const = 0;
};
class triangle : public face {
private:
std::vector<ptr> vertices;
public:
std::vector<ptr>amp; get_vertices(void) const { return vertices; };
};
class quadrilateral : public face {
private:
std::vector<ptr> vertices;
public:
std::vector<ptr>amp; get_vertices(void) const { return vertices; };
};
Очевидно, что треугольник и четырехугольник всегда будут иметь 3 и 4 вершины соответственно.
Таким образом, я хотел бы заменить std::vector на std::array соответствующего размера, чтобы сэкономить накладные расходы, вызванные тремя дополнительными указателями в std::vector. (Это потому, что у меня будут миллионы граней …) Теперь, есть ли шанс иметь общую функцию доступа совместно с std:: array, как с std:: vector выше?
Со стандартным C-массивом я бы просто вернул указатель на первую запись массива и ее размер. Есть ли STL-способ сделать то же самое или что-то подобное? Или есть какой-либо другой хороший способ достичь этой функциональности?
Спасибо за чтение и, возможно, за ответ!! Андреас
Комментарии:
1. О чем
additional pointers in std::vector
вы говорите?2. Вам действительно нужно возвращать их в виде контейнера STL? Если есть только 3 или 4 элемента, я не вижу большого преимущества в векторе или массиве. Я бы просто сохранил указатели в массиве фиксированного размера и использовал некоторую функцию или оператор, который возвращает элементы напрямую, например
virtual ptramp; operator[](unsigned idx);
, и другую функцию для получения их количества.3. @Nawaz: Реализация std::vector (по крайней мере, для gcc) использует три указателя для начала хранения, завершения хранения и окончания хранения в дополнение к самим сохраненным данным. Это становится не пренебрегаемыми накладными расходами, если у вас есть огромное количество std :: vectors с относительно небольшим количеством записей.
4. @Timo: Это решение, о котором я думал, но оно не кажется «приятным» решением, подобным STL.
Ответ №1:
std::arrays
разные размеры — это разные типы, поэтому я не вижу никакого способа сделать то, что вы хотите. Но что, если вы вернете итераторы begin()
и end()
const в любой контейнер, который вы храните внутри, или в объект малого диапазона, содержащий оба? Таким образом, вы отделяете размер контейнера от интерфейса, оставляя его на усмотрение реализации.
РЕДАКТИРОВАТЬ: Просто для пояснения, чтобы скрыть представление хранилища данных (в данном случае размер std::array
), вам понадобится ваш собственный класс итератора для face
. Это легко реализовать с точки зрения указателей, поскольку для каждой face
специализации вам известны размер, начало и конец базовой структуры данных. Но вы, очевидно, не можете напрямую использовать std::array
‘s begin()
и end()
в интерфейсе.
Пример:
Это быстрый и грязный пример, иллюстрирующий, как реализовать часть поведения прямого итератора с использованием указателей. Я использую виртуальный базовый класс и одну из реализаций из OP в качестве отправной точки. Я также опустил все конструкторы, операторы присваивания и т.д. Он также предполагает наличие ребра класса (предположительно, 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. Это кажется подходящим решением. Однако я подумал, что может быть «более приятное» решение, т. Е. уже реализован некоторый итератор, не зависящий от размера массива. В любом случае, большое спасибо!!
2. @Andreas, я добавил простой пример, просто чтобы дать представление о простой реализации. Пожалуйста, отнеситесь к этому с осторожностью, это было сделано в спешке!
Ответ №2:
std::array
здесь, похоже, нет решения, поскольку get_vertices
это чисто виртуальная функция, это означает, что вы хотели бы получить к ней доступ, используя указатель (или ссылку) типа базового класса. И если вы используете std::array
, вы должны предоставить целочисленное значение в качестве второго аргумента для std::array
шаблона класса, что возможно, если вы создадите face
шаблон класса, что-то вроде этого:
template<size_t N>
class face {
virtual std::array<ptr, N>amp; get_vertices(void) const = 0;
};
class triangle : public face<3>{
//...
std::array<ptr, 3>amp; get_vertices(void) const { return vertices; };
};
class quadrilateral : public face<4> {
//...
std::array<ptr, 4>amp; 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>amp; 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()
, и вы добавите больше элементов, чем его емкость. Изменение размера не происходит.
Комментарии:
1. Как и в моем комментарии выше, это спасает меня только от «дорогостоящих» перераспределений, но не от вызванных накладных расходов std::vector.