Предположим, что мне нужно сохранить коллекцию объектов того же типа, но этот тип не может быть определен во время компиляции. Предположим также, что, как только этот тип определен, он никогда не изменяется. Как хорошо известно, когда тип не знает, во время компиляции, можно сохранить эти объекты, используя контейнер указателей на их базовый класс, т.е.C++ Контейнер полиморфных объектов с общим vptr
std::vector<Base*> collection;
collection.push_back(new Derived());
Таким образом, выделенные объекты не будут обязательно сохранены бок о бок в памяти, потому что в каждом распределении new()
вернет произвольное положение в памяти. Кроме того, есть дополнительный указатель (vptr
), встроенный в каждый объект, потому что класс Base
, конечно, должен быть полиморфным.
В данном конкретном случае (тип определяется один раз + тип никогда не изменяется), указанное решение не является оптимальным решением, потому что, теоретически,
- это не нужно хранить так же
vptr
(SizeOf() = размер указателя) для каждого объекта: все они указывают на то же самоеvtable
; - можно использовать непрерывные места хранения, так как размер объектов определяется в начале программы и никогда не будет меняться.
Q: ли вы, ребята, знаете стратегию/контейнер/распределитель памяти/идиому/трюк/что-нибудь еще, чтобы преодолеть эти проблемы?
Я думаю, что я мог бы сделать что-то вроде этого (с использованием классического Shape пример):
struct Triangle_Data {
double p1[3],p2[3],p3[3];
};
struct Circle_Data {
double radius;
};
struct Shape {
virtual double area() const = 0;
virtual char* getData() = 0;
virtual ~Shape() {}
};
struct Triangle : public Shape {
union {
Triangle_Data tri_data;
char data[sizeof(Triangle_Data)];
};
double area() const { /*...*/ };
char* getData() { return data; }
Triangle(char * dat_) {
std::copy(dat_, dat_+sizeof(Triangle_Data), this->data);
};
};
struct Circle : public Shape {
union {
Circle_Data circ_data;
char data[sizeof(Circle_Data)];
};
double area() const { /*...*/ };
char* getData() { return data; }
Circle(char * dat_) {
std::copy(dat_, dat_+sizeof(Circle_Data), this->data);
};
};
template<class BaseT>
struct Container {
int n_objects;
int sizeof_obj;
std::vector<char> data;
Container(...arguments here...) : ...init here... {
data.resize(sizeof_obj * n_objects);
}
void push_back(Shape* obj) {
// copy the content of obj
for(int i=0; i<sizeof_obj; ++i)
data.push_back(*(obj.getData() + i));
}
char* operator[] (int idx) {
return data + idx*sizeof_obj;
}
};
// usage:
int main() {
Container<Shape> collection(..init here..);
collection.push_back(new Circle());
cout << Circle(collection[0]).area() << endl; // horrible, but does it work?
};
Конечно, этот подход имеет много проблем с безопасностью типа, выравнивание и т.п .. Любое предложение ?
Спасибо
Если вы запрашиваете полиморфизм во время выполнения, то есть виртуальные функции, вы не можете не платить цену за _'vptr'_ (уникальная функция _'vtable'_) – StoryTeller
Можно было бы использовать язык для обеспечить эту оптимизацию. C++ - нет. –
«Базовый класс, конечно, должен быть виртуальным». Нет такого понятия, как «виртуальный» класс. Существуют только полиморфные классы (и виртуальные базы, что совсем другое)./nitpick – curiousguy