假设我需要存储一个相同类型的对象的集合,但是这个类型不能在编译时定义。还假设一旦定义了这种类型,它就永远不会改变。众所周知,当编译时不知道类型时,可以使用指向其基类的指针容器来存储这些对象,即
std::vector<Base*> collection;
collection.push_back( new Derived() );
这样,分配的对象不一定会并排存储在内存中,因为在每次分配中new()
都会返回内存中的任意位置。此外,每个对象都嵌入了一个额外的指针 ( vptr
),因为Base
类当然需要是多态的。
对于这种特殊情况(类型定义一次 + 类型永不改变),上述解决方案不是最佳解决方案,因为理论上,
- 不必
vptr
为每个对象存储相同的 (sizeof() = 指针大小):它们都指向相同的vtable
; - 可以使用连续的存储位置,因为对象的大小是在程序开始时定义的,并且永远不会改变。
问:你们知道克服这些问题的策略/容器/内存分配器/成语/技巧/其他任何东西吗?
我想我可以做这样的事情(使用经典的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?
};
当然,这种方法在类型安全、对齐等方面存在很多问题。有什么建议吗?
谢谢