Достаточно ли просто "просто" расположить их в одном и том же порядке в самом начале?
Да, если вы будете осторожны, как делали выше.
также, как спроектировать заголовок метода для его принятия?
Существуют различные способы сделать это.
Вот пример, использующий [уродливый]эквивалент 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(..)
?
Нет проблем с free
, если выделения выполняются с производными типами (например, frame
, button
), но не базовый тип 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 *next
элемент к struct geo
.Затем мы могли бы сделать:
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
, чтобы каждая фигура знала, в какой форме она содержится.