Всех приветствую хочу с Вами посоветоватся относительно языка С а точнее хочу что бы Вы поделились опытом относительно организации интерфейса для различных ADT возмем для примера очередь для всех примеров чтобы сконцентрироватся именно на интерфейсе я часто вижу три варианта реализации интерфейса вариант 1 в заголовочном файле определяем структуру Код (Text): typedef queue { //... все поля видимы для кода //... который использует интерфейс } queue; удобно в плане прямого доступа к нужным полям но некоторые поля можно было бы и скрыть так как они используются только для реализации другие же очень удобно использовать напрямую в клиентском коде вариант 2 в заголовочном файле определяем структуру но не со всеми полями которые реально существуют в объекте а только с теми к которым нужно и удобно обращатся из клиентского кода например так сделано в GLIB пример Код (Text): typedef queue { //...только то что нужно видеть в интерфейсе } queue; а в файле реализации уже определяем полную структуру Код (Text): typedef real_queue { //...интерфейсная чаcть //...служебные данные } real_queue; queue* dummy(queue *q) { real_queue qe = (real_queue*)q; // ...processing ... return (queue*)qe; } так реализованы некоторые ADT в GLIB интересный подход в клиентском коде есть доступ к внутренним полям но только к видимым и которые реально нужны бывают в клиентском коде вариант 3 встречаются и такие реализации в заголовочном файле объявляем неполный тип Код (Text): struct queue; фактически в клиентском коде можно использовать только указатель и только для прима перадачи его интерфейсу кто что использует ? может можно как то лучше делать ? я конечно понимаю что по большей части выбор представления определяет сам ADT с учетом его характеристик но хотелось бы Вас послушать и чему то научится у Вас
typedef struct { ... } common_interface_t; typedef struct { common_interface_t common; ..... } specific_thing_1_t; typedef struct { common_interface_t common; ..... } specific_thing_2_t; ... #define SPECIFY_THIS_THING(typ, ptr) ((typ*)ptr) #define INTERFACE_THIS_THING(ptr) (&(ptr->common)) итд. такой подход используется много где. например, в параметрах Х вынь сообщений. в связи лимбо адт и С частей. кстати, откуда словцо то? я такое название классов мало где встречал.
изврат ? Код (Text): /* Object Orientation in C */ /* Contributed by Jim Weirich */ /* Edit osox */ #include <stdio.h> #include <stdlib.h> /* abstract interface declaration */ struct Shape { struct ShapeFuncTable *funcTable; void *privateData; }; struct ShapeFuncTable { void (*Draw)(struct Shape * obj); void (*MoveTo)(struct Shape * obj, int newx, int newy); void (*RMoveTo)(struct Shape * obj, int dx, int dy); }; /* Class Rectangle */ struct RectanglePrivateData { int x, y; int width; int height; }; void RectangleDraw (struct Shape * obj) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; printf ("Drawing a Rectangle at (%d,%d), width %d, height %d\n", rdata->x, rdata->y, rdata->width, rdata->height); } void RectangleMoveTo (struct Shape * obj, int newx, int newy) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->x = newx; rdata->y = newy; } void RectangleRMoveTo (struct Shape * obj, int dx, int dy) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->x += dx; rdata->y += dy; } void RectangleSetWidth (struct Shape * obj, int newWidth) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->width = newWidth; } void RectangleSetHeight (struct Shape * obj, int newHeight) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->height = newHeight; } struct RectangleFuncTable { void (*Draw)(struct Shape * obj); void (*MoveTo)(struct Shape * obj, int newx, int newy); void (*RMoveTo)(struct Shape * obj, int dx, int dy); void (*SetWidth)(struct Shape * obj, int width); void (*SetHeight)(struct Shape * obj, int height); } rectangleFuncTable = { RectangleDraw, RectangleMoveTo, RectangleRMoveTo, RectangleSetWidth, RectangleSetHeight }; struct Shape * MakeRectangle ( int initx, int inity, int initw, int inith) { struct Shape * obj = malloc (sizeof *obj); struct RectanglePrivateData * rdata = malloc (sizeof *rdata); obj->funcTable = (struct ShapeFuncTable*) &rectangleFuncTable; obj->privateData = rdata; rdata->x = initx; rdata->y = inity; rdata->width = initw; rdata->height = inith; return obj; } /* Class Circle */ struct CirclePrivateData { int x, y; int radius; }; void CircleDraw (struct Shape * obj) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; printf ("Drawing a Circle at (%d,%d), radius %d\n", cdata->x, cdata->y, cdata->radius); } void CircleMoveTo (struct Shape * obj, int newx, int newy) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; cdata->x = newx; cdata->y = newy; } void CircleRMoveTo (struct Shape * obj, int dx, int dy) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; cdata->x += dx; cdata->y += dy; } void CircleSetRadius (struct Shape * obj, int newRadius) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; cdata->radius = newRadius; } struct CircleFuncTable { void (*Draw)(struct Shape * obj); void (*MoveTo)(struct Shape * obj, int newx, int newy); void (*RMoveTo)(struct Shape * obj, int dx, int dy); void (*SetRadius)(struct Shape * obj, int width); } circleFuncTable = { CircleDraw, CircleMoveTo, CircleRMoveTo, CircleSetRadius }; struct Shape * MakeCircle (int initx, int inity, int initr) { struct Shape * obj = malloc (sizeof *obj); struct CirclePrivateData * cdata = malloc (sizeof *cdata); obj->funcTable = (struct ShapeFuncTable*) &circleFuncTable; obj->privateData = cdata; cdata->x = initx; cdata->y = inity; cdata->radius = initr; return obj; } /* =================================================================== * DoSomethingWithShape is a fuction that takes a polymorphic shape * and manipulates it according to its interface. It doesn't care if * shape is a circle or a rectangle, as long as it conforms to the * shape interface. * * If we would pull the shape interface into a separate header file * and move this function into its own ".c" file (that referenced the * Shape only definitions), then we could add new shapes without * changing a single line of source code in this function, yet the * function would still handle shapes that were defined by the user. */ void DoSomethingWithShape (struct Shape * s) { s->funcTable->Draw (s); s->funcTable->RMoveTo (s, 100, 100); s->funcTable->Draw (s); } /* =================================================================== * Main Program */ void test_draw(struct Shape *t) { t->funcTable->Draw(t); } typedef struct Shape Rectangle; typedef struct Shape Circle; void Draw(struct Shape *Obj) { Obj->funcTable->Draw(Obj); } void SetWidth (struct Shape * obj, int newWidth) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->width = newWidth; } юзать так Код (Text): int main () { Rectangle *Rect = MakeRectangle (10, 20, 5, 6); Circle *Circ = MakeCircle (15, 25, 8); /* using shapes polymorphically */ Draw(Rect); /* req->pVtbl->Draw() */ Draw(Circ); /* circ->pVtbl->Draw() */ /* access a rectangle specific function */ SetWidth(Rect, 250); /* req->pPrivateData->width */ /* using shapes polymorphically */ Draw(Rect); /* req->pVtbl->Draw() */ return 0; } вот еще бы как нибудь избавится от передачи ссылки на объект тоесть не так Draw(Rect); /* req->pVtbl->Draw() */ Draw(Circ); /* circ->pVtbl->Draw() */ а так Rect.Draw(); /* req->pVtbl->Draw() */ Circ.Draw(); /* circ->pVtbl->Draw() */
osox да нет. нормально. только декларацию типа и выделение/заполнение переменных лучше разделять. и для доступа к вариантам лучше использовать макросы. во первых писать меньше, а во вторых читабельнее W4FhLF J0E некоторые оч распространенные языки программирования позволяют следующие конструкции a = 2 a = "n = " + (a + 3) a = ( "".join(a, "\n-------------"), 18) print a[0] зачем использовать с++ где все так сложно по сравнению с ^? как вы догадываетесь, на уровне реализации это самый настоящий полиморфизм. (причем, писано там все на с). причем, в беседах с адептами вышеприведенного языка постоянно сталкиваешься с ихним полным нежеланием вникать в детали реализации, даже ради получаемых от этого бонусов. в разговорах они, как правило, постоянно применяют магическое слово "классы", которых на уровне вм как раз и нет. но увидеть это не вникая в подробности реализации невозможно. если подобных полиморфизмов немного, а код достаточно сложнее хеловорда, то отказ от с++ увеличит скорость и уменьшит размеры. кроме того, с более портабелен и портирован чем с++. например, я знаю небольшие встраиваемые и пригодные для встраивания компиляторы с, но ни одного с++. далее, код на с может быть достаточно легко перенесен на многие языки-потомки. чего не скажешь о с++. таким образом, с++ это не лучший, а просто другой вариант. есть и другие другие. но вопрос был по С. ответ, видимо, ожидается тоже по С
Что бы иметь статическую типизацию, перенести часть ошибок из run time в compile time. Голословное утверждение. За счет чего? Это под Windows Вопрос был про полиморфизм времени компиляции, ответ был про полиморфизм времени выполнения. Мой намек был изучить разницу.