Как я могу имитировать полиморфизм OO-стиля в C?
есть ли способ написать OO-подобный код в C
язык программирования?
Читайте также:
найдено путем поиска на "[c] oo".
9 ответов:
первый компилятор C++ ("C с классами") фактически генерирует код C, так что это определенно выполнимо.
в принципе, ваш базовый класс является структурой; производные структуры должны включать базовую структуру в первую позицию, так что указатель на "производную" структуру также будет действительным указателем на базовую структуру.
typedef struct { data member_x; } base; typedef struct { struct base; data member_y; } derived; void function_on_base(struct base * a); // here I can pass both pointers to derived and to base void function_on_derived(struct derived * b); // here I must pass a pointer to the derived class
функции могут быть частью структуры в качестве указателей функций, так что синтаксис, такой как P - > call(p) становится возможным, но вы все равно должны явно передавать указатель на структуру самой функции.
общий подход заключается в определении структуры с указателями на функции. Это определяет "методы", которые могут быть вызваны на любом типе. Затем подтипы устанавливают свои собственные функции в этой общей структуре и возвращают ее.
например, в ядре linux есть struct:
struct inode_operations { int (*create) (struct inode *,struct dentry *,int, struct nameidata *); struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *); ... };
каждый зарегистрированный тип файловой системы затем регистрирует свои собственные функции для
create
,lookup
, и оставшиеся функции. Остальная часть кода может использовать общие inode_operations:struct inode_operations *i_op; i_op -> create(...);
C++ не так далеко от C.
классы-это структуры со скрытым указателем на таблицу указателей функций, называемую VTable. Сама таблица является статической. Когда типы указывают на Vtables с той же структурой, но где указатели указывают на другую реализацию, Вы получаете полиморфизм.
рекомендуется инкапсулировать логику вызовов в функции, которые принимают структуру в качестве параметра, чтобы избежать беспорядка кода.
вы также должны инкапсулировать структуры создание экземпляров и инициализация в функциях (это эквивалентно конструктору C++) и удаление (деструктор в C++). Это хорошая практика в любом случае.
typedef struct { int (*SomeFunction)(TheClass* this, int i); void (*OtherFunction)(TheClass* this, char* c); } VTable; typedef struct { VTable* pVTable; int member; } TheClass;
для вызова метода:
int CallSomeFunction(TheClass* this, int i) { (this->pVTable->SomeFunction)(this, i); }
Я посмотрел на ответы всех остальных и придумал это:
#include <stdio.h> typedef struct { int (*get)(void* this); void (*set)(void* this, int i); int member; } TheClass; int Get(void* this) { TheClass* This = (TheClass*)this; return This->member; } void Set(void* this, int i) { TheClass* This = (TheClass*)this; This->member = i; } void init(TheClass* this) { this->get = &Get; this->set = &Set; } int main(int argc, char **argv) { TheClass name; init(&name); (name.set)(&name, 10); printf("%d\n", (name.get)(&name)); return 0; }
Я надеюсь, что ответы на некоторые вопросы.
приложение в к статье Открыть Многоразовые Объектные Модели, Ян Пьюмарта и Алессандро Варт из VPRI - это реализация объектной модели в GNU C, около 140 строк кода. Это увлекательное чтение !
вот некэшированная версия макроса, который отправляет сообщения объектам, используя расширение GNU для C (выражение):
struct object; typedef struct object *oop; typedef oop *(*method_t)(oop receiver, ...); //... #define send(RCV, MSG, ARGS...) ({ \ oop r = (oop)(RCV); \ method_t method = _bind(r, (MSG)); \ method(r, ##ARGS); \ })
в том же документе, посмотрите на
object
,vtable
,vtable_delegated
иsymbol
структуры, а_bind
иvtable_lookup
функции.Ура!
файловые функции fopen, fclose, fread являются примерами кода OO в C. Вместо частных данных в классе они работают над файловой структурой, которая используется для инкапсуляции данных, а функции C действуют как функции класса-члена. http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016
Из Википедии: В языках программирования и теории типов полиморфизм (от греческого πολύς, Polys, "много, много" и μορφή, Morphē, "форма, форма") - это предоставление единого интерфейса для сущностей разных типов.
поэтому я бы сказал, что единственный способ реализовать его в C - это использовать вариативные аргументы вместе с некоторым (полу)автоматическим управлением информацией о типе. Например в C++ можно написать (извините за банальность):
void add( int& result, int a1, int a2 ); void add( float& result, float a1, float a2 ); void add( double& result, double a1, double a2 );
В C, среди других решений, лучшие вы можете сделать что-то вроде этого:
int int_add( int a1, int a2 ); float float_add( float a1, fload a2 ); double double_add( double a1, double a2 ); void add( int typeinfo, void* result, ... );
тогда тебе нужно:
- для реализации "typeinfo" с перечислениями / макросами
- для реализации последней функции с помощью stdarg.ч то
- попрощаться с проверкой статического типа C
я почти уверен, что любая другая реализация полиморфизма должна выглядеть как один. Вместо этого приведенные выше ответы, похоже, пытаются решить проблему наследования более чем полиморфизм!
#include <stdio.h> typedef struct { int x; int z; } base; typedef struct { base; int y; int x; } derived; void function_on_base( base * a) // here I can pass both pointers to derived and to base { printf("Class base [%d]\n",a->x); printf("Class base [%d]\n",a->z); } void function_on_derived( derived * b) // here I must pass a pointer to the derived class { printf("Class derived [%d]\n",b->y); printf("Class derived [%d]\n",b->x); } int main() { derived d; base b; printf("Teste de poliformismo\n"); b.x = 2; d.y = 1; b.z = 3; d.x = 4; function_on_base(&b); function_on_base(&d); function_on_derived(&b); function_on_derived(&d); return 0; }
вывод:
Class base [3] Class base [1] Class base [4] Class derived [2] Class derived [3] Class derived [1] Class derived [4]
так это работает, его полиморфный код.
дядя Зеев объяснил об этом в самом начале.
чтобы тоже построить функциональность OO в C, вы можете посмотреть предыдущие ответы.
но, (как было задано в других вопросах, перенаправленных на этот), если вы хотите понять, что такое полиморфизм, на примерах на языке C. Может быть, я ошибаюсь, но я не могу придумать ничего столь же понятны, как указатели c арифметикой. По-моему,арифметика указателя по своей сути полиморфна в C. В следующем примере та же функция (метод в OO), а именно добавление ( + ), будет производить различное поведение в зависимости от свойств входных структур.
пример:
double a*; char str*; a=(double*)malloc(2*sizeof(double)); str=(char*)malloc(2*sizeof(char)); a=a+2; // make the pointer a, point 2*8 bytes ahead. str=str+2; // make the pointer str, point 2*1 bytes ahead.
отказ от ответственности: я очень новичок в C и очень надеюсь на исправление и изучение комментариев других пользователей или даже полностью стереть этот ответ, если это будет неправильно. Большое спасибо,