C++ вектор с динамическим размером элемента
Вектор C++ STL имеет много приличных свойств, но работает только тогда, когда размер каждого элемента известен во время выполнения.
Я хотел бы иметь векторный класс, который показывает динамический размер элемента во время выполнения.
Фон: мои предметы состоят из последовательности целых и двойных чисел; последовательность, которая известна только во время выполнения. Было бы достаточно, чтобы вектор был задан размером каждого элемента во время выполнения.
Я знаю о возможных обходных путях, но они, как правило, не отражают основную идею алгоритма, что всегда плохо в отношении сопровождения. Существуют ли классы, которые обеспечивают такое удобство и работают так эффективно, как можно было бы ожидать?
РЕДАКТИРОВАТЬ:
Речь идет не о размерах предметов, варьирующихся по всему массиву. Это не имеет ничего общего с этим. Во время выполнения решается, насколько велики элементы в массиве; то есть (очень) слабая форма динамической типизации, в отличие от статической типизации, используемой с шаблонами.
Следовательно, инициализация объекта должна выглядеть так:
DynamicVector V( lengthofvector, sizeofelement );
Приложение - это симплициальные сетки. Объект $V$ содержит элементы фиксированного размера или "типа", каждый из которых состоит из целых чисел для топологической информации и удваивается для некоторой геометрической информации. Могут даже появиться логические выражения, но это пока не имеет значения.
9 ответов
Проблема в том, что если у вас нет способа сохранить размер каждого элемента в векторе, вы никогда не сможете получить данные обратно.
Как насчет просто хранить ВСЕ предметы как double
? Это резко упрощает вещи.
По-другому вы могли бы рассмотреть boost::variant
,
РЕДАКТИРОВАТЬ: Но вы можете объяснить, почему вы хотите хранить два разных типа в одной последовательности? Иногда это может указывать на то, что основополагающий дизайн нуждается в дальнейшем осмыслении
Вы могли бы использовать vector
указателей на ваш объект последовательности - предпочтительно умные указатели, чтобы упростить управление памятью в векторе.
Если это просто последовательность int
а также double
, то вы можете просто использовать:
std::vector<double> sequence;
а затем вставить int
а также double
внутрь. Тем не менее, этот подход не отслеживает тип элементов. Если тип критичен для вас, то, вероятно, вам может помочь следующее:
struct item
{
union
{
int i;
double d;
} data;
char type; //store 0 for int, 1 for double;
};
std::vector<item> sequence;
Конечно, такой подход обходится вам как минимум в один дополнительный байт на элемент для хранения типа элемента. Вы можете использовать #pragma pack
методы, чтобы выжать дополнительную прокладку.
Или, что еще лучше, измените дизайн своего кода так, чтобы вместо одной последовательности было две последовательности:
std::vector<int> intSeq;
std::vector<double> doubleSeq;
Вектор представляет собой непрерывный массив в памяти. Это эффективно, поскольку может использовать арифметику указателей для прямого доступа к элементам по индексу. Но при этом подразумевается, что все элементы имеют одинаковый размер, и нужно знать этот размер перед построением вектора.
Для ваших нужд используйте вектор указателей на элементы из двойного связанного списка. Вектор указателей почти так же быстро, как вектор. Это требует только одного режима уважения.
Если ваши целые и двойные числа имеют одинаковый размер (например, 64 бита), вы можете использовать объединение для доступа к каждому элементу в виде целого числа или двойного числа. Но вам нужен способ узнать, каким должен быть каждый элемент.
Создайте класс, который содержит три вектора: один для целых чисел, один для двойников и один, который имеет запись для каждого элемента, чтобы указать тип соответствующего элемента и его индекс в соответствующем векторе.
Если вы не хотите прибегать к обходным путям, я думаю, вам следует подумать об абстракции этих "элементов динамического размера". Он должен быть спроектирован с учетом того, что он должен использоваться внутри вектора STL (тогда должны быть гарантированы некоторые реквизиты), так что вы можете наконец написать что-то вроде:
std::vector<DynamicSizedItem> myVector;
Я видел этот вопрос раньше! Есть ли контейнер STL, который хранит массив элементов в непрерывной памяти, где размер элемента указан во время выполнения?
Гай хотел "чередующийся вектор" (его слово), который бы содержал объекты динамического размера, определяемые картой типов элементов и смещений:
typedef Toffset uint; //byte offset;
typedef Ttype uint; //enum of types
typedef std::pair<Toffset,Ttype> member;
typedef std::unordered_map<std::string, member> memberdefs;
И я придумал (непроверенный) класс, чтобы справиться с этим. Полный код в ссылке, но прототипы:
class interleaved_vector {
const char* buffer;
size_t count;
size_t size;
std::shared_ptr<memberdefs> members;
public:
class dynamic_object {
const char* buffer;
std::shared_ptr<memberdefs> members;
friend interleaved_vector;
dynamic_object(const char* buffer_, std::shared_ptr<memberdefs> members_);
dynamic_object& operator=(const dynamic_object& b) = delete;
public:
dynamic_object(const dynamic_object& b) ;
template <class T>
T get(const std::string& member) const;
template <>
T* get<T*>(const std::string& member) const;
void* operator[](const std::string& member) const;
};
interleaved_vector(const char* buffer_, size_t count_, size_t size_, const memberdefs& members_);
dynamic_object get(size_t index) const;
dynamic_object operator[](size_t index) const;
size_t size();
};
В качестве предупреждения: оно опирается на какое-то поведение, которое, на мой взгляд, не определено и вообще является плохой идеей. Перейти с вектором указателей.
Используйте std::vector, где item - это умный указатель с переносом. Класс "item" делает указатель похожим на простое значение:
class item {
private:
boost:unique_ptr<base> p;
public:
// ...
public item(item that);
public int someFunction() {
// Forwarded
return p->somefunction();
}
};
class base {
virtual ~base();
// This is needed in order to implement copy of the item class
virtual base* clone();
};
public item::item(item that) : p(that.p.clone()) {}
class some_real_type() : public base {
// ....
}
Я думаю, что лучший путь вперед (с точки зрения производительности и обслуживания) - это обходной путь, при котором вы помещаете std::vector и std::vector в два класса, которые объединяют общий базовый класс с соответствующим интерфейсом.
Если вы хотите, чтобы это было динамически во время выполнения, вот как это сделать правильно. Это также поможет вам написать эффективный код для обработки каждого элемента, а также просто (но медленно) получить доступ к каждому элементу.