Які існують способи написання об'єктно-орієнтованого коду на C? Особливо щодо поліморфізму.
Дивіться також це питання переповнення стека об'єктно-орієнтоване програмування в C .
Які існують способи написання об'єктно-орієнтованого коду на C? Особливо щодо поліморфізму.
Дивіться також це питання переповнення стека об'єктно-орієнтоване програмування в C .
Відповіді:
Так. Насправді Аксель Шрайнер надає свою книгу "Об'єктно-орієнтоване програмування в ANSI-C" безкоштовно, яка досить ретельно висвітлює тему.
Оскільки ви говорите про поліморфізм, то так, ви можете, ми робили подібні речі багато років до появи С ++.
В основному ви використовуєте a, struct
щоб утримувати як дані, так і список покажчиків функцій, щоб вказати на відповідні функції для цих даних.
Отже, у класі комунікацій ви мали б відкритий, читати, записувати та закривати виклик, який підтримувався б як чотири функціональні вказівники в структурі поряд із даними для об'єкта, як-от:
typedef struct {
int (*open)(void *self, char *fspec);
int (*close)(void *self);
int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
// And data goes here.
} tCommClass;
tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;
tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;
Звичайно, ці сегменти коду, що були вище, насправді були б у "конструкторі", такому як rs232Init()
.
Коли ви 'успадковуєте' від цього класу, ви просто змінюєте покажчики, щоб вказати на власні функції. Усі, хто викликав ці функції, зробили б це за допомогою покажчиків функцій, надаючи вам свій поліморфізм:
int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");
Начебто вручну vtable.
Ви навіть можете мати віртуальні класи, встановивши покажчики на NULL - поведінка буде дещо відрізнятися від C ++ (основний дамп у процесі виконання, а не помилка під час компіляції).
Ось фрагмент зразкового коду, який демонструє це. Спочатку структура класу вищого рівня:
#include <stdio.h>
// The top-level class.
typedef struct sCommClass {
int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;
Тоді у нас є функції для підкласу TCP:
// Function for the TCP 'class'.
static int tcpOpen (tCommClass *tcp, char *fspec) {
printf ("Opening TCP: %s\n", fspec);
return 0;
}
static int tcpInit (tCommClass *tcp) {
tcp->open = &tcpOpen;
return 0;
}
А також HTTP:
// Function for the HTTP 'class'.
static int httpOpen (tCommClass *http, char *fspec) {
printf ("Opening HTTP: %s\n", fspec);
return 0;
}
static int httpInit (tCommClass *http) {
http->open = &httpOpen;
return 0;
}
І нарешті тестова програма, щоб показати її в дії:
// Test program.
int main (void) {
int status;
tCommClass commTcp, commHttp;
// Same 'base' class but initialised to different sub-classes.
tcpInit (&commTcp);
httpInit (&commHttp);
// Called in exactly the same manner.
status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
status = (commHttp.open)(&commHttp, "http://www.microsoft.com");
return 0;
}
Це дає результат:
Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com
тож ви бачите, що викликаються різні функції залежно від підкласу.
tCommClass
буде перейменований в tCommVT
, аtCommClass
структура матиме лише поля даних і одне tCommVT vt
поле, що вказує на "єдину" єдину віртуальну таблицю. Перенесення всіх покажчиків із кожним екземпляром додає зайвих накладних витрат і більше нагадує те, як ви робите речі в JavaScript, ніж C ++, IMHO.
Простори імен часто роблять:
stack_push(thing *)
замість
stack::push(thing *)
Щоб перетворити структуру С на щось на зразок класу C ++, ви можете перетворити:
class stack {
public:
stack();
void push(thing *);
thing * pop();
static int this_is_here_as_an_example_only;
private:
...
};
В
struct stack {
struct stack_type * my_type;
// Put the stuff that you put after private: here
};
struct stack_type {
void (* construct)(struct stack * this); // This takes uninitialized memory
struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
int this_is_here_as_an_example_only;
}Stack = {
.construct = stack_construct,
.operator_new = stack_operator_new,
.push = stack_push,
.pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else
І роби:
struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
// Do something about it
} else {
// You can use the stack
stack_push(st, thing0); // This is a non-virtual call
Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
st->my_type.push(st, thing2); // This is a virtual call
}
Я не робив деструктора і не видаляв, але це відповідає тій же схемі.
this_is_here_as_an_example_only - це як статична змінна класу - спільна для всіх примірників типу. Усі методи дійсно статичні, за винятком того, що деякі приймають це *
st->my_type->push(st, thing2);
замістьst->my_type.push(st, thing2);
struct stack_type my_type;
замістьstruct stack_type * my_type;
Class
структури? Це зробило б O C більш динамічним, ніж C ++. Як щодо цього? До речі, +1.
Я вважаю, що, крім того, що сама по собі є корисною, реалізація OOP в C - це відмінний спосіб навчитись ООП та зрозуміти його внутрішню роботу. Досвід багатьох програмістів показав, що для використання методики ефективно та впевнено, програміст повинен розуміти, як в кінцевому рахунку реалізуються основні концепції. Емуляція класів, успадкування та поліморфізм у С вчить саме цьому.
Щоб відповісти на початкове запитання, ось декілька ресурсів, які вчать робити OOP в C:
Повідомлення в блозі EmbeddedGurus.com "Об'єктне програмування на C" показує, як реалізувати класи та єдине успадкування в портативному C: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
Примітка до застосування "" C + "- об'єктно-орієнтоване програмування на C" показує, як реалізувати класи, єдине успадкування та пізнє зв'язування (поліморфізм) у C за допомогою макросів препроцесора: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , приклад коду доступний на веб-сайті http://www.state-machine.com/resources/cplus_3.0.zip
Я бачив це зроблено. Я б не рекомендував це. C ++ спочатку розпочав цей шлях як препроцесор, який створив код C як проміжний крок.
По суті, ви в кінцевому підсумку створюєте диспетчерську таблицю для всіх своїх методів, де ви зберігаєте посилання на свої функції. Виведення класу призведе до копіювання цієї диспетчерської таблиці та заміни записів, які ви хотіли переотримати, при цьому ваші нові "методи" повинні викликати оригінальний метод, якщо він хоче викликати базовий метод. Зрештою, ви закінчите переписувати C ++.
glib
написано на С об'єктивно?
Підбібліотека C stdio FILE є прекрасним прикладом того, як створити абстракцію, інкапсуляцію та модульність у непорушеному С.
Спадщина та поліморфізм - інші аспекти, які часто вважаються важливими для ООП, - не обов'язково забезпечують підвищення продуктивності, яку вони обіцяють, і були зроблені обґрунтовані аргументи , які можуть насправді перешкоджати розвитку та роздуму над проблемою.
Тривіальний приклад із твариною та собакою: ви відображаєте механізм V + (в основному все-таки). Ви також розділяєте розподіл та інстанції (Animal_Alloc, Animal_New), щоб ми не називали malloc () кілька разів. Ми також повинні явно пропустити this
вказівник навколо.
Якщо ви мали би виконувати невіртуальну функцію, це дрібниця. Ви просто не додаєте їх до vtable, а статичні функції не потребують this
вказівника. Для усунення неоднозначностей багатократне успадкування вимагає декількох таблиць.
Крім того, ви повинні мати можливість використовувати setjmp / longjmp для обробки виключень.
struct Animal_Vtable{
typedef void (*Walk_Fun)(struct Animal *a_This);
typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);
Walk_Fun Walk;
Dtor_Fun Dtor;
};
struct Animal{
Animal_Vtable vtable;
char *Name;
};
struct Dog{
Animal_Vtable vtable;
char *Name; // Mirror member variables for easy access
char *Type;
};
void Animal_Walk(struct Animal *a_This){
printf("Animal (%s) walking\n", a_This->Name);
}
struct Animal* Animal_Dtor(struct Animal *a_This){
printf("animal::dtor\n");
return a_This;
}
Animal *Animal_Alloc(){
return (Animal*)malloc(sizeof(Animal));
}
Animal *Animal_New(Animal *a_Animal){
a_Animal->vtable.Walk = Animal_Walk;
a_Animal->vtable.Dtor = Animal_Dtor;
a_Animal->Name = "Anonymous";
return a_Animal;
}
void Animal_Free(Animal *a_This){
a_This->vtable.Dtor(a_This);
free(a_This);
}
void Dog_Walk(struct Dog *a_This){
printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}
Dog* Dog_Dtor(struct Dog *a_This){
// Explicit call to parent destructor
Animal_Dtor((Animal*)a_This);
printf("dog::dtor\n");
return a_This;
}
Dog *Dog_Alloc(){
return (Dog*)malloc(sizeof(Dog));
}
Dog *Dog_New(Dog *a_Dog){
// Explict call to parent constructor
Animal_New((Animal*)a_Dog);
a_Dog->Type = "Dog type";
a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;
return a_Dog;
}
int main(int argc, char **argv){
/*
Base class:
Animal *a_Animal = Animal_New(Animal_Alloc());
*/
Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());
a_Animal->vtable.Walk(a_Animal);
Animal_Free(a_Animal);
}
PS. Це тестується на компіляторі C ++, але це може бути легко змусити його працювати на компіляторі C.
typedef
Усередині struct
НЕ представляється можливим в С.
Перевірте GObject . Це покликане бути OO в C і одна реалізація того, що ви шукаєте. Якщо ви дійсно хочете OO, перейдіть з C ++ або іншою мовою OOP. GObject може бути дуже важким для роботи часом, якщо ви звикли мати справу з мовами OO, але, як що завгодно, ви звикнете до конвенцій і потоків.
Це було цікаво читати. Я сам розмірковував над тим самим питанням, і переваги роздуму над цим:
Спроба уявити, як реалізувати концепції OOP мовою, що не є ООП, допомагає мені зрозуміти сильні сторони мови OOp (у моєму випадку C ++). Це допомагає мені краще оцінити, чи потрібно використовувати C або C ++ для даного типу додатків - коли переваги одного переважають іншого.
Під час перегляду Інтернету для отримання інформації та думок з цього приводу я знайшов автора, який писав код для вбудованого процесора і мав доступний лише компілятор C: http://www.easures.com/discussion/other/4024626/Object-Oriented -С-Створення-Фундація-Класи-Частина-1
У його випадку аналіз та адаптація концепцій ООП у простому С було вагомим пошуком. Здається, він був готовий пожертвувати деякими концепціями ООП через накладний удар, спричинений результатом спроби їх впровадження у C.
Урок, який я взяв, - так, це можна зробити певною мірою, і так, є кілька вагомих причин для цього.
Врешті-решт, машина перекручує біти вказівника стека, завдяки чому лічильник програм стрибає навколо та обчислює операції доступу до пам'яті. З точки зору ефективності, чим менше цих розрахунків зроблено вашою програмою, тим краще ... але іноді нам доводиться сплачувати цей податок просто, щоб ми могли організувати нашу програму таким чином, щоб зробити її найменш схильною до людських помилок. Компілятор мови OOP прагне оптимізувати обидва аспекти. Програміст повинен бути набагато обережнішим, реалізовуючи ці концепції такою мовою, як C.
Вам може бути корисним переглянути документацію Apple щодо її набору API API фундаментального характеру. Це чистий API API, але багато типів пов'язані з еквівалентами об'єктів Objective-C.
Також вам може виявитися корисним подивитися на саму конструкцію Objective-C. Це трохи відрізняється від C ++ тим, що об'єктна система визначається в термінах функцій C, наприклад, objc_msg_send
для виклику методу на об'єкті. Компілятор переводить синтаксис квадратної дужки в ці виклики функції, тому вам не доведеться цього знати, але, враховуючи своє запитання, вам може бути корисним дізнатися, як він працює під кришкою.
Є кілька прийомів, які можна використовувати. Найголовніше - це більше, як розділити проект. У нашому проекті ми використовуємо інтерфейс, який оголошується у файлі .h та реалізацію об’єкта у файлі .c. Важлива частина полягає в тому, що всі модулі, що включають .h файл, бачать лише об'єкт якvoid *
, а .c файл - єдиний модуль, який знає внутрішню структуру.
Щось подібне для класу, який ми називаємо FOO як приклад:
У файлі .h
#ifndef FOO_H_
#define FOO_H_
...
typedef struct FOO_type FOO_type; /* That's all the rest of the program knows about FOO */
/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif
Файл реалізації C буде чимось подібним.
#include <stdlib.h>
...
#include "FOO.h"
struct FOO_type {
whatever...
};
FOO_type *FOO_new(void)
{
FOO_type *this = calloc(1, sizeof (FOO_type));
...
FOO_dosomething(this, );
return this;
}
Тому я вказую вказівник явно на об'єкт на кожну функцію цього модуля. Компілятор C ++ робить це неявно, а в C - це явно виписуємо.
Я дуже використовую this
в своїх програмах, щоб переконатися, що моя програма не компілюється в C ++, і вона має прекрасну властивість бути іншим кольором у моєму редакторі виділення синтаксису.
Поля FOO_struct можуть бути змінені в одному модулі, а інший модуль навіть не потрібно перекомпілювати, щоб використовувати його.
З цим стилем я вже вирішую значну частину переваг OOP (інкапсуляція даних). Використовуючи функціональні покажчики, навіть легко реалізувати щось на кшталт спадкування, але, чесно кажучи, це дійсно корисно лише рідко.
typedef struct FOO_type FOO_type
замість typedef скасуєте недійсність у заголовку, ви отримаєте додаткову перевагу перевірки типу, при цьому все ще не наражаючи на вашу структуру.
Ви можете підробити це за допомогою функціональних покажчиків, і насправді, я думаю, теоретично можливо компілювати програми C ++ у C.
Однак, рідко є сенс накладати парадигму на мову, а не вибирати мову, яка використовує парадигму.
Об'єктно-орієнтований C, можна зробити, я бачив цей тип коду у виробництві в Кореї, і це було найжахливіше монстр, який я бачив за роки (це було як у минулому році (2007), коли я бачив код). Так так, це можна зробити, і так люди робили це раніше, і досі це роблять навіть у цей день та вік. Але я б рекомендував C ++ або Objective-C, обидві - це мови, які народилися з C, з метою надання орієнтації на об'єкт з різними парадигмами.
Якщо ви переконані, що підхід OOP є кращим за проблему, яку ви намагаєтеся вирішити, чому б ви намагалися вирішити її мовою, яка не є OOP? Схоже, ви використовуєте неправильний інструмент для роботи. Використовуйте C ++ або якусь іншу об'єктно-орієнтовану мову C-варіанту.
Якщо ви запитуєте, оскільки ви починаєте кодувати вже існуючий великий проект, написаний на C, тоді вам не слід намагатися примушувати власні (чи чужі) парадигми OOP до інфраструктури проекту. Дотримуйтесь вказівок, які вже є у проекті. В цілому, чисті інтерфейси і ізольовані бібліотеки і модулі будуть йти довгий шлях до маючи чисту OOP- іш дизайн.
Якщо після всього цього ви дійсно налаштовані на виконання OOP C, прочитайте це (PDF).
Так, ти можеш. Люди писали об'єктно-орієнтований C перед C ++ або Objective-C на місце події вийшов . І C ++, і Objective-C почасти були спробами прийняти деякі поняття OO, що використовуються в C, та формалізувати їх як частину мови.
Ось справді проста програма, яка показує, як ви можете зробити щось, що схоже / є викликом методу (є кращі способи зробити це. Це лише доказ того, що мова підтримує ці поняття):
#include<stdio.h>
struct foobarbaz{
int one;
int two;
int three;
int (*exampleMethod)(int, int);
};
int addTwoNumbers(int a, int b){
return a+b;
}
int main()
{
// Define the function pointer
int (*pointerToFunction)(int, int) = addTwoNumbers;
// Let's make sure we can call the pointer
int test = (*pointerToFunction)(12,12);
printf ("test: %u \n", test);
// Now, define an instance of our struct
// and add some default values.
struct foobarbaz fbb;
fbb.one = 1;
fbb.two = 2;
fbb.three = 3;
// Now add a "method"
fbb.exampleMethod = addTwoNumbers;
// Try calling the method
int test2 = fbb.exampleMethod(13,36);
printf ("test2: %u \n", test2);
printf("\nDone\n");
return 0;
}
Невеликий код OOC, який потрібно додати:
#include <stdio.h>
struct Node {
int somevar;
};
void print() {
printf("Hello from an object-oriented C method!");
};
struct Tree {
struct Node * NIL;
void (*FPprint)(void);
struct Node *root;
struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};
int main()
{
struct Tree TreeB;
TreeB = TreeA;
TreeB.FPprint();
return 0;
}
Я копав це один рік:
Оскільки системою GObject важко користуватися чистим C, я спробував написати кілька приємних макросів, щоб полегшити стиль OO за допомогою C.
#include "OOStd.h"
CLASS(Animal) {
char *name;
STATIC(Animal);
vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
THIS->name = name;
return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)
CLASS_EX(Cat,Animal) {
STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
printf("Meow!My name is %s!\n", THIS->name);
}
static int Cat_loadSt(StAnimal *THIS, void *PARAM){
THIS->talk = (void *)Meow;
return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)
CLASS_EX(Dog,Animal){
STATIC_EX(Dog, Animal);
};
static void Woof(Animal *THIS){
printf("Woof!My name is %s!\n", THIS->name);
}
static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
THIS->talk = (void *)Woof;
return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)
int main(){
Animal *animals[4000];
StAnimal *f;
int i = 0;
for (i=0; i<4000; i++)
{
if(i%2==0)
animals[i] = NEW(Dog,"Jack");
else
animals[i] = NEW(Cat,"Lily");
};
f = ST(animals[0]);
for(i=0; i<4000; ++i) {
f->talk(animals[i]);
}
for (i=0; i<4000; ++i) {
DELETE0(animals[i]);
}
return 0;
}
Ось мій сайт проекту (у мене не вистачає часу для написання en. Doc, проте док на китайській мові набагато краще).
Є приклад успадкування з використанням C у виступі Джима Ларсона 1996 року, наведеному на Семінарі по обідню програмі в розділі 312 тут: C і високий низький рівень .
У яких статтях чи книгах добре використовувати поняття OOP в C?
Інтерфейси та реалізація Дейва Хансона - це чудовий спосіб інкапсуляції та іменування та дуже хороший у використанні функціональних покажчиків. Дейв не намагається імітувати спадщину.
Одне, що ви можете зробити, - це вивчити реалізацію інструментарію Xt для X Window . Звичайно, що зуб стає довгим, але багато використовуваних конструкцій були розроблені для роботи в рамках OO в рамках традиційного С. Взагалі це означає додавання зайвого шару опосередкування тут і там і проектування конструкцій, що лежать одна над одною.
Ви дійсно можете зробити багато на шляху OO, розташованого в C таким чином, хоча це відчувається так, як це часом, поняття OO не сформувалися повністю з розуму #include<favorite_OO_Guru.h>
. Вони справді становили багато усталених кращих практик того часу. Мови та системи OO лише дистильовані та посилені частини програмного дня.
Відповідь на питання: "Так, можна".
Об'єктно-орієнтований набір C (OOC) призначений для тих, хто хоче програмувати об'єктно-орієнтованим способом, але дотримується і старого доброго С. OOC реалізує класи, одинарне та багаторазове успадкування, обробку винятків.
Особливості
• Використовує лише C макроси та функції, не потрібні розширення мови! (ANSI-C)
• Легкий для читання вихідний код для вашої програми. Дбали про те, щоб зробити речі максимально простими.
• Однократне успадкування класів
• Багаторазове успадкування за допомогою інтерфейсів та комбінацій (починаючи з версії 1.3)
• Реалізація винятків (у чистому мові C!)
• Віртуальні функції для занять
• Зовнішній інструмент для легкої реалізації класу
Для отримання більш детальної інформації відвідайте http://ooc-coding.sourceforge.net/ .
Схоже, люди намагаються наслідувати стиль C ++ за допомогою C. Я вважаю, що виконання об'єктно-орієнтованого програмування C справді займається структурно-орієнтованим програмуванням. Однак ви можете домогтися таких речей, як пізня зв'язування, капсулювання та успадкування. Для успадкування ви чітко визначаєте вказівник на базові структури у вашій підструктурі, і це, очевидно, форма множинного успадкування. Вам також потрібно буде визначити, чи є ваш
//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);
//private_class.c
struct inherited_class_1;
struct inherited_class_2;
struct private_class {
int a;
int b;
struct inherited_class_1 *p1;
struct inherited_class_2 *p2;
};
struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();
struct private_class * new_private_class() {
struct private_class *p;
p = (struct private_class*) malloc(sizeof(struct private_class));
p->a = 0;
p->b = 0;
p->p1 = new_inherited_class_1();
p->p2 = new_inherited_class_2();
return p;
}
int ret_a_value(struct private_class *p, int a, int b) {
return p->a + p->b + a + b;
}
void delete_private_class(struct private_class *p) {
//release any resources
//call delete methods for inherited classes
free(p);
}
//main.c
struct private_class *p;
p = new_private_class();
late_bind_function = &implementation_function;
delete_private_class(p);
компілювати с c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.
Тож порада - дотримуватися чистого стилю C, а не намагатися примушувати до стилю C ++. Також цей спосіб піддається дуже чистому способу створення API.
Дивіться http://slkpg.byethost7.com/instan.html для ще одного повороту на OOP в C. Це підкреслює дані про екземпляри для повторного працевлаштування за допомогою просто рідного C. Багатократне успадкування здійснюється вручну за допомогою функціональних обгортків. Безпека типу підтримується. Ось невеликий зразок:
typedef struct _peeker
{
log_t *log;
symbols_t *sym;
scanner_t scan; // inherited instance
peek_t pk;
int trace;
void (*push) ( SELF *d, symbol_t *symbol );
short (*peek) ( SELF *d, int level );
short (*get) ( SELF *d );
int (*get_line_number) ( SELF *d );
} peeker_t, SlkToken;
#define push(self,a) (*self).push(self, a)
#define peek(self,a) (*self).peek(self, a)
#define get(self) (*self).get(self)
#define get_line_number(self) (*self).get_line_number(self)
INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
return d->scan.line_number;
}
PUBLIC
void
InitializePeeker ( peeker_t *peeker,
int trace,
symbols_t *symbols,
log_t *log,
list_t *list )
{
InitializeScanner ( &peeker->scan, trace, symbols, log, list );
peeker->log = log;
peeker->sym = symbols;
peeker->pk.current = peeker->pk.buffer;
peeker->pk.count = 0;
peeker->trace = trace;
peeker->get_line_number = get_line_number;
peeker->push = push;
peeker->get = get;
peeker->peek = peek;
}
Я трохи запізнююся на вечірку, але хочу поділитися своїм досвідом з теми: Я працюю з вбудованими речами в наші дні, і єдиний (надійний) компілятор у мене є C, так що я хочу застосувати об'єктно-орієнтований підхід до моїх вбудованих проектів, написаних на С.
Більшість рішень, які я бачив до цього часу, сильно використовують typecasts, тому ми втрачаємо безпеку типу: компілятор не допоможе вам, якщо ви помилитесь. Це абсолютно неприпустимо.
Вимоги, які у мене є:
Я детально пояснив свій підхід у цій статті: Об'єктно-орієнтоване програмування на C ; плюс, є утиліта для автогенерації кодового шаблону для базових та похідних класів.
Я побудував маленьку бібліотеку, де я спробував це, і для мене це працює чудово. Тому я подумав, що ділюсь досвідом.
https://github.com/thomasfuhringer/oxygen
Одинакове успадкування може бути реалізовано досить легко, використовуючи структуру та розширюючи її для кожного іншого дочірнього класу. Простий податок на батьківську структуру дає можливість використовувати батьківські методи для всіх нащадків. Поки ви знаєте, що змінна вказує на структуру, що містить такий тип об'єкта, ви завжди можете передавати кореневий клас і робити інтроспекцію.
Як вже було сказано, віртуальні методи дещо складніші. Але вони виконані. Щоб зробити все просто, я просто використовую масив функцій в структурі опису класу, який кожен дочірній клас копіює та перенаселяє окремі слоти, де це потрібно.
Багаторазове успадкування було б досить складним у здійсненні та має значний вплив на ефективність. Тому я залишаю це. Я вважаю бажаним і корисним у досить багатьох випадках чітко моделювати реальні життєві обставини, але, мабуть, у 90% випадків одне успадкування покриває потреби. А поодиноке успадкування просте і нічого не коштує.
Також я не дбаю про безпеку типу. Я думаю, вам не слід залежати від компілятора, щоб не допустити помилок програмування. І це все одно захищає вас від досить невеликої частини помилок.
Як правило, в об'єктно-орієнтованому середовищі ви також хочете реалізувати підрахунок посилань для автоматизації управління пам'яттю настільки, наскільки це можливо. Тож я також поклав підрахунок посилань до кореневого класу «Об’єкт» та деяку функціональність для інкапсуляції розподілу та розподілу пам’яті купи.
Це все дуже просто і худорляво і дає мені основи OO, не змушуючи мене мати справу з монстром, що є C ++. І я зберігаю гнучкість перебування на землі С, що, серед іншого, полегшує інтеграцію сторонніх бібліотек.
Я пропоную використовувати Objective-C, який є надмножиною C.
Хоча Objective-C 30 років, він дозволяє писати елегантний код.
Так, але я ніколи не бачив, щоб хтось намагався здійснити якийсь поліморфізм із С.