Как я могу симулировать полиморфизм OO-стиля в C? - PullRequest
68 голосов
/ 07 февраля 2009

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


Смотри также:

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

Ответы [ 9 ]

59 голосов
/ 07 февраля 2009

Первый компилятор 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), но вам все равно придется явно передавать указатель на структуру самой функции.

51 голосов
/ 07 февраля 2009

Общий подход заключается в определении структуры с указателями на функции. Это определяет «методы», которые могут быть вызваны для любого типа. Затем подтипы устанавливают свои собственные функции в этой общей структуре и возвращают ее.

Например, в ядре linux есть структура:

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(...);
27 голосов
/ 07 февраля 2009

C ++ не так далеко от C.

Классы - это структуры со скрытым указателем на таблицу указателей функций, которая называется VTable. Сам 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);
}
15 голосов
/ 24 сентября 2012

Я посмотрел ответы всех остальных и придумал:

#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;
}

Я надеюсь, что это ответит на некоторые вопросы.

12 голосов
/ 08 февраля 2009

Приложение B к статье Открытые многоразовые объектные модели , автор Ian Piumarta и Alessandro Warth из 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.

Ура!

1 голос
/ 09 апреля 2016
#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]

так что это работает, это полиморфный код.

UncleZeiv объяснил об этом в начале.

1 голос
/ 07 февраля 2009

Файловые функции fopen, fclose, fread являются примерами OO-кода на C. Вместо закрытых данных в классе они работают со структурой FILE, которая используется для инкапсуляции данных, а функции C действуют как функции класса-члена. , http://www.amazon.com/File-Structures-Object-Oriented-Approach-C/dp/0201874016

0 голосов
/ 12 марта 2017

Чтобы тоже построить ОО-функциональность в C, вы можете посмотреть предыдущие ответы.

Но (как было задано в других вопросах, перенаправленных на этот вопрос), если вы хотите понять, что такое полиморфизм, на примерах на языке Си. Может быть, я ошибаюсь, но я не могу придумать ничего более простого для понимания, чем арифметика указателей Си. По моему мнению, арифметика указателей по своей природе полиморфна в C . В следующем примере та же функция (метод в ОО), а именно сложение (+), будет вызывать различное поведение в зависимости от свойств входных структур.

Пример:

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 и очень жду, когда меня исправят и учатся на комментариях других пользователей, или даже полностью удаляют этот ответ, если он будет неправильным. Большое спасибо,

0 голосов
/ 22 октября 2014

Из Википедии: В языках программирования и теории типов полиморфизм (от греческого πολύς, 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.h stuff
  3. прощаться с статической проверкой типа C

Я почти уверен, что любая другая реализация полиморфизма должна выглядеть так же, как эта. Вместо этого приведенные выше ответы, кажется, пытаются адресовать наследование больше, чем полиморфизм!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...