一开始就“只是”将它们按相同的顺序排列就足够了吗?
是的,如果你像上面所做的那样小心的话。
另外,如何设计方法头来接受它?
有不同的方法可以做到这一点。
下面是一个示例,使用 [ugly] 等效的 c++“基”类:
enum type {
FRAME,
BUTTON,
WHATEVER
};
struct geo {
int x;
int y;
int width;
int height;
enum type type;
};
struct frame {
struct geo geo;
};
struct button {
struct geo geo;
int updown;
};
struct whatever {
struct geo geo;
int what;
int ever;
};
void
frame_render(struct geo *geo)
{
struct frame *frm;
struct button *but;
struct whatever *what;
switch (geo->type) {
case FRAME:
frm = (struct frame *) geo;
frame_render_frame(frm);
break;
case BUTTON:
but = (struct button *) geo;
printf("x=%d y=%d updown=%d\n",geo->x,geo->y,but->updown);
frame_render_button(but);
break;
case WHATEVER:
what = (struct whatever *) geo;
printf("x=%d y=%d what=%d ever=%d\n",
what->geo.x,what->geo.y,what->what,what->ever);
frame_render_whatever(what);
break;
}
}
下面是一个使用虚函数表的方法:
enum type {
FRAME,
BUTTON,
WHATEVER
};
struct geo;
// virtual function table
struct virtfnc {
void (*calc)(struct geo *);
void (*render)(struct geo *);
};
struct geo {
int x;
int y;
int width;
int height;
enum type type;
struct virtfnc *fnc;
};
struct frame {
struct geo geo;
};
struct button {
struct geo geo;
int updown;
};
struct whatever {
struct geo geo;
int what;
int ever;
};
void
frame_render(struct geo *geo)
{
struct frame *frm = (struct frame *) geo;
// whatever
}
void
button_render(struct geo *geo)
{
struct button *but = (struct button *) geo;
// whatever
}
void
whatever_render(struct geo *geo)
{
struct whatever *what = (struct whatever *) geo;
// whatever
}
void
any_render(struct geo *geo)
{
geo->fnc->render(geo);
}
这是使用union 的第三种方式。它更简单,但要求基础结构与最大的子类一样大:
enum type {
FRAME,
BUTTON,
WHATEVER
};
struct frame {
...
};
struct button {
int updown;
};
struct whatever {
int what;
int ever;
};
struct geo {
int x;
int y;
int width;
int height;
enum type type;
union {
struct frame frame;
struct button button;
struct whatever what;
} data;
};
void
any_render(struct geo *geo)
{
switch (geo->type) {
case FRAME:
render_frame(&geo->data.frame);
break;
case BUTTON:
render_button(&geo->data.button);
break;
case WHATEVER:
render_whatever(&geo->data.what);
break;
}
}
更新:
这种方法铸造安全吗?例如。将所有内容放入frame* 类型的数组中,然后访问frame->geo?或者这会导致以后调用free(..) 时出现任何问题?
如果分配是使用派生类型(例如frame、button)完成的,free 没有问题,但不是基本类型geo :malloc(sizeof(struct button)).
要拥有一个简单 [形状] 数组,需要使用union 方法(即所有派生结构必须具有相同的大小)。但是,如果我们有一些子类型比其他子类型使用 很多 更多空间,这将是浪费:
struct polyline {
int num_points;
int x[100];
int y[100];
};
这可以仍然用方法#1或#2[子类型结构的大小不同]用一个间接指针数组来完成:
void
all_render(struct geo **glist,int ngeo)
{
for (; ngeo > 0; --ngeo, ++glist)
any_render(*glist);
}
我会考虑使用 [双重] 链表,而不是一组不同形状的数组。这允许子类型结构具有不同的大小。我们将向struct geo 添加一个struct geo *next 元素。然后,我们可以这样做:
void
all_render(struct geo *geo)
{
for (; geo != NULL; geo = geo->next)
any_render(geo);
}
列表方法可能更可取,特别是如果我们动态添加/删除形状[或根据 Z 深度重新排序它们]。
或者,某些形状可能包含其他形状。所以,我们可以将struct geo *children 添加到struct geo。然后,很容易(例如)绘制一个包含框,然后通过遍历children 列表来绘制该框内的所有形状。如果我们选择children,我们也可以添加struct parent *parent,这样每个形状都知道它包含什么形状。