ООП и язык C
Эта статья продолжает тему имитации C++ на чистом C. В данной статье я расскажу как в стиле чистого C организовать обработку данных различного типа, которые могут находится "под одной крышей", например в односвязных или двусвязных списках.
Итак предположим, что у нас есть структура данных описывающая точку в двух координатах, и есть структура данных описывающая точку в трёх координатах. Стандартное C++ решение выглядит так:
class Point2D { public: float x,y; Point2D() { x = y = 0; } virtual void Draw(){ coutDraw(); }
Итак класс Point3D наследует класс Point2D.
Есть виртуальный метод Draw. Предположим, что у нас есть список, элементами которого являются указатели на обьекты как типа Point2D, так и типа Point3D. И есть функция "обёртка" DrawXObject вызывающая метод Draw для любого из обьектов. Мы вызываем Draw через указатель на базовый класс, и так как метод виртуальный, то для каждого обьекта вызовится своя реализация метода. Что нам и нужно. Всё красиво и просто.
Но это в C++. Попробуем повторить это по сишному.
Кому код приведённый ниже покажется не понятным, то читайте предыдущую статью, а эту отложите до лучших времён. Дальше код, а потом обьяснения. Как и в прошлый раз BorlandC++Builder4.0 -> Console Application
#pragma hdrstop #include #include #include #pragma argsused enum TAGS { t_Point2D, t_Point3D }; extern "C" void Point2D_method_Draw(void* _this); extern "C" void Point2D_method_Init(void* _this); typedef void (*Point2D_fn_Init)(void*); typedef void (*Point2D_fn_Draw)(void*); typedef struct { int tag; float x, y; static Point2D_fn_Init Init; static Point2D_fn_Draw Draw; }Point2D; Point2D_fn_Init Point2D :: Init = Point2D_method_Init; Point2D_fn_Draw Point2D :: Draw = Point2D_method_Draw; extern "C" void Point3D_method_Draw(void* _this); extern "C" void Point3D_method_Init(void* _this); typedef void (*Point3D_fn_Init)(void*); typedef void (*Point3D_fn_Draw)(void*); typedef struct { int tag; float x, y, z; static Point3D_fn_Init Init; static Point3D_fn_Draw Draw; }Point3D; Point3D_fn_Init Point3D :: Init = Point3D_method_Init; Point3D_fn_Draw Point3D :: Draw = Point3D_method_Draw; void Point2D_method_Init(void* _this) { Point2D* t = (Point2D*)_this; t->tag = t_Point2D; t->x = t->y = 0.0; } void Point2D_method_Draw(void* _this) { Point2D* t = (Point2D*)_this; printf("Poin2D Draw "); } void Point3D_method_Init(void* _this) { Point3D* t = (Point3D*)_this; t->tag = t_Point3D; t->x = t->y = t->z = 0.0; } void Point3D_method_Draw(void* _this) { Point3D* t = (Point3D*)_this; printf("Poin3D Draw "); } void DrawXObject(void* obj) { int tag_obj = *((int*)obj); if(tag_obj == t_Point2D){ Point2D* p = (Point2D*)obj; p->Draw(&p); } else if(tag_obj == t_Point3D){ Point3D* p = (Point3D*)obj; p->Draw(&p); } } int main(int argc, char* argv[]) { Point2D p2d; Point3D p3d; p2d.Init(&p2d); p3d.Init(&p3d); DrawXObject(&p2d); DrawXObject(&p3d); getch(); return 0; }
В структурах обьявлена переменная tag, которая инициализируется в функциях Init. Эта переменная понадобится нам в последствии, для того, чтобы определить, с каким обьектом мы имеем дело. В функции DrawXObject мы получаем значение этой переменной и принимаем решение к какому типу преобразовать полученный функцией указатель.
Помоему ничуть не хуже C++ реализации.
Оставить комментарий
Комментарии
Нет, понимаю, можно вообще все на ассемблере писать. Или уж сразу в машинных кодах, в hex-редакотре... Но зачем? Компьютеры для того и существуют, чтобы всю рутину можно было поручить им...
И что это за конструкция?
[skip]
Point3D_fn_Init Point3D :: Init = Point3D_method_Init;
Point3D_fn_Draw Point3D :: Draw = Point3D_method_Draw;
[skip]
Стандарт C не описывает "::".