2009-08-06 2 views
34

Можно ли моделировать наследование с помощью C? Как? Пример кода поможет.Как можно наследовать, используя C?

Редактировать: Я ищу для наследования как данных, так и методов. Контейнеры сами по себе не помогут. Подстановка - использование любого объекта производного класса, где работает объект базового класса, - это то, что мне нужно.

+0

Какова причина, что делать? –

+0

У меня есть код C, использующий в основном контейнеровозы. Проект достиг точки, в которой заменимость («отношение») стала очень желательной. –

+0

в основном отвечает на все ваши вопросы: [http://stackoverflow.com/questions/415452/object-orientation-in-c/415536#415536](http://stackoverflow.com/questions/415452/object-orientation-in -c/415536 ​​# 415536) – Junier

ответ

3

Должно быть возможно, по крайней мере, до некоторой степени.

Что именно вам нужно для моделирования? Наследование данных или методов?

Редактировать: Вот короткая статья, что я нашел: http://fluff.info/blog/arch/00000162.htm

1

Эта ссылка может быть полезно ->link

Базовый пример будет, как следовать

struct BaseStruct 
{ 
    // some variable 
} 


struct DerivedStruct 
{ 
    struct BaseStruct lw; 
    // some more variable 
}; 
2

С ранних версий C++ были в основном препроцессором, который преобразовывался в C, это действительно возможно.

20

Это очень просто идти, как это:

struct parent { 
    int foo; 
    char *bar; 
}; 

struct child { 
    struct parent base; 
    int bar; 
}; 

struct child derived; 

derived.bar = 1; 
derived.base.foo = 2; 

Но если вы используете расширение MS (в GCC использовать -fms-extensions флаг), вы можете использовать анонимные вложенные struct с и это будет выглядеть намного лучше:

struct child { 
    struct parent; // anonymous nested struct 
    int bar; 
}; 

struct child derived; 

derived.bar = 1; 
derived.foo = 2;  // now it is flat 
+0

приятно, но теперь CLang не будет компилировать это. – makapuf

+0

@makapuf C11 поддерживает его как стандартную функцию (https://en.wikipedia.org/wiki/C11_(C_standard_revision)#Changes_from_C99), поэтому 'clang' должен поддерживать это с '-std = c11' – qrdl

+0

ну, точно не похоже. C11 принимает его с анонимными немаркированными структурами, поэтому вы можете вставлять другую неназванную структуру, но не можете повторно использовать предыдущее определение структуры ... (почему это так, я не знаю). – makapuf

2

Я использовал объектную систему в C, которая использовала методы поздней связи, что позволило объектно-ориентированной ориентации с отражением.

Вы можете прочитать об этом here.

+0

действительно; объект слишком широк, чтобы полностью покрыть один ответ на stackoverflow. На первый взгляд, я думаю, что статья, связанная с этим, охватывает немало. Мне нравится объяснение Миро Самека в «Практических государственных диаграммах в C/C++» (только 1-е издание) – Adriaan

8

Вы можете определенно написать C в (несколько) объектно-ориентированном стиле.

Инкапсуляция может быть выполнена путем хранения определений ваших структур в .c файле, а не в соответствующем заголовке. Затем внешний мир обрабатывает ваши объекты, сохраняя указатели на них, , и вы предоставляете функции, принимающие такие указатели, как «методы» ваших объектов.

полиморфизм типа поведения могут быть получены с помощью функции указателей, обычно группируются в «операции структур», вроде как «виртуальный метод таблицы» в вашем C++ объекты (или как там это называется). Структура ops также может включать в себя другие вещи, такие как константы, значение которых специфично для данного «подкласса». «Родительская» структура может содержать ссылку на данные, специфичные для ОП, через общий указатель void*. Конечно, «подкласс» может повторять шаблон для нескольких уровней наследования.

Так, в приведенном ниже примере, struct printer сродни абстрактный класс, , который может быть «производной», заполнив pr_ops структуру, и предоставление функции конструктора оберточную pr_create(). Каждый подтип будет иметь свою собственную структуру, которая будет «привязана» к объекту struct printer через data общий указатель. Это демонизируется подтипом fileprinter. Можно представить графический интерфейс или принтер на основе сокетов, , который будет обрабатываться независимо от остальной части кода в качестве ссылки struct printer *.

printer.h:

struct pr_ops { 
    void (*printline)(void *data, const char *line); 
    void (*cleanup)(void *data); 
}; 

struct printer *pr_create(const char *name, const struct output_ops *ops, void *data); 
void pr_printline(struct printer *pr, const char *line); 
void pr_delete(struct printer *pr); 

printer.c:

#include "printer.h" 
... 

struct printer { 
    char *name; 
    struct pr_ops *ops; 
    void *data; 
} 

/* constructor */ 
struct printer *pr_create(const char *name, const struct output_ops *ops, void *data) 
{ 
    struct printer *p = malloc(sizeof *p); 
    p->name = strdup(name); 
    p->ops = ops; 
    p->data = data; 
} 

void pr_printline(struct printer *p, const char *line) 
{ 
    char *l = malloc(strlen(line) + strlen(p->name) + 3; 
    sprintf(l, "%s: %s", p->name, line); 
    p->ops->printline(p->data, l); 
} 

void pr_delete(struct printer *p) 
{ 
    p->ops->cleanup(p->data); 
    free(p); 
} 

Наконец, fileprinter.c:

struct fileprinter { 
    FILE *f; 
    int doflush; 
}; 

static void filepr_printline(void *data, const char *line) 
{ 
    struct fileprinter *fp = data; 
    fprintf(fp->f, "%s\n", line); 
    if(fp->doflush) fflush(fp->f); 
} 

struct printer *filepr_create(const char *name, FILE *f, int doflush) 
{ 
    static const struct ops = { 
     filepr_printline, 
     free, 
    }; 

    struct *fp = malloc(sizeof *fp); 
    fp->f = f; 
    fp->doflush = doflush; 
    return pr_create(name, &ops, fp); 
} 
+8

+1. Также как историческая заметка - первые компиляторы C++ не выводили машинный код, а C-код, потому что он был быстрее и проще в реализации (C-машинный кодовый слой существующий). Таким образом, это может быть сделано в C. – TomTom

1
#include <stdio.h> 

///////Class Cobj 
typedef struct Cobj{ 
    int x; 
    void (*setptr)(char * s,int val); 
    int (*getptr)(char * s); 
} Cobj; 

void set(char * s,int val) 
{ 
    Cobj * y=(Cobj *)s; 
    y->x=val; 
} 
int get(char * s){ 
    Cobj * y=(Cobj *)s; 
    return y->x; 
} 
///////Class Cobj 
Cobj s={12,set,get}; 
Cobj x; 

void main(void){ 
    x=s; 
    x.setptr((char*)&x,5); 
    s.setptr((char*)&s,8); 
    printf("%d %d %d",x.getptr((char*)&x),s.getptr((char*)&s) ,sizeof(Cobj)); 
} 
1

Да, вы можете эмулировать наследс- ан C используя метод «пинания типа». Это объявление базового класса (структуры) внутри производного класса, и литой производное в качестве основы:

struct base_class { 
    int x; 
}; 

struct derived_class { 
    struct base_class base; 
    int y; 
} 

struct derived_class2 { 
    struct base_class base; 
    int z; 
} 
void test() { 
    struct derived_class d; 
    struct derived_class2 d2; 

    d.base.x = 10; 
    d.y = 20; 

    printf("x=%i, y=%i\n", d.base.x, d.y); 
} 

Но вы должны объявить базовый класс в первом положении в вас производную структуре, если вы хотите лишить производную базу в программе:

struct base *b1, *b2; 

b1 = (struct base *)d; 
b2 = (struct base *)d2; 

b1->x=10; 
b2->x=20; 
printf("b1 x=%i, b2 x=%i\n", b1->x, b2->x); 

В этом фрагменте вы можете использовать только базовый класс.

Я использую эту технику в своих проектах: oop4c

+0

Не должно быть & d и & d2? –

Смежные вопросы