Как я могу имитировать полиморфизм OO-стиля в C?


есть ли способ написать OO-подобный код в C язык программирования?


Читайте также:

найдено путем поиска на "[c] oo".

9 57

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, ... );

тогда тебе нужно:

  1. для реализации "typeinfo" с перечислениями / макросами
  2. для реализации последней функции с помощью stdarg.ч то
  3. попрощаться с проверкой статического типа 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 и очень надеюсь на исправление и изучение комментариев других пользователей или даже полностью стереть этот ответ, если это будет неправильно. Большое спасибо,