Можете ли вы написать объектно-ориентированный код на C? [закрыто]

Если вы хотите включить параметры регулярного выражения (например, игнорировать), попробуйте следующее:

import re
regx = re.compile("^foo", re.IGNORECASE)
db.users.find_one({"files": regx})
468
задан 7 revs, 3 users 36% 23 May 2017 в 12:18
поделиться

22 ответа

Да. На самом деле Axel Schreiner обеспечивает его книга "Объектно-ориентированное программирование в ANSI-C" бесплатно, который покрывает предмет вполне полностью.

343
ответ дан 6 revs, 6 users 50% 23 May 2017 в 12:18
поделиться

Существует пример наследования с помощью C в докладе Jim Larson 1996 года, сделанном в Раздел 312 Семинаров Ланча Программирования здесь: Высокий и Низкий уровень C .

4
ответ дан 3 revs 23 May 2017 в 12:18
поделиться

Тривиальный пример с Животным и Собакой: Вы зеркально отражаете vtable механизм C++ (в основном так или иначе). Вы также разделяете выделение и инстанцирование (Animal_Alloc, Animal_New), таким образом, мы не называем malloc () многократно. Мы должны также явно раздать this указатель.

, Если необходимо было сделать невиртуальные функции, это - trival. Вы просто не добавляете их к vtable и статическим функциям, не требуют this указатель. Множественное наследование обычно требует, чтобы несколько vtables разрешили неоднозначности.

кроме того, необходимо быть в состоянии использовать 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);
}

пз. Это тестируется на компиляторе C++, но должно быть легко заставить его работать над компилятором C.

15
ответ дан 2 revs, 2 users 73% 23 May 2017 в 12:18
поделиться

Объектно-ориентированный C, может быть сделан, я видел, что тип кода в производстве в Корее, и это был самый ужасный монстр, которого я видел в годах (это было похоже на прошлый год (2007), что я видел код). Так да это может быть сделано, и да, люди сделали его прежде, и все еще делают это даже в этот день и возраст. Но я рекомендовал бы C++ или Objective C, оба - языки, перенесенные от C, с целью предоставления объектной ориентации с различными парадигмами.

7
ответ дан Robert Gould 23 May 2017 в 12:18
поделиться

Да, но я никогда не видел, что любой пытается реализовать любой вид полиморфизма с C.

0
ответ дан Paul Morel 23 May 2017 в 12:18
поделиться

Уверенный это возможно. Это - то, что GObject, делает платформа, на основе которой все из GTK + и GNOME.

25
ответ дан 3 revs, 2 users 53% 23 May 2017 в 12:18
поделиться

Да, Вы можете. Люди писали объектно-ориентированный 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;
}
6
ответ дан 2 revs, 2 users 80% 23 May 2017 в 12:18
поделиться

Я видел сделанный. Я не рекомендовал бы его. C++ первоначально запустил этот путь как препроцессор, который произвел код C как промежуточный шаг.

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

32
ответ дан tvanfosson 23 May 2017 в 12:18
поделиться

Можно фальсифицировать его с помощью указателей функции, и на самом деле, я думаю, что теоретически возможно скомпилировать программы C++ в C.

Однако редко имеет смысл вызывать парадигму на языке, а не выбирать язык, который использует парадигму.

7
ответ дан Uri 23 May 2017 в 12:18
поделиться

Так как Вы говорите о полиморфизме тогда да, Вы можете, мы делали такой материал за годы до того, как C++ появился.

В основном Вы используете 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

, таким образом, Вы видите, что различные функции вызываются, в зависимости от подкласса.

321
ответ дан 6 revs, 2 users 92% 23 May 2017 в 12:18
поделиться

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

6
ответ дан 2 revs, 2 users 67% 23 May 2017 в 12:18
поделиться

Возможно, вам будет полезно просмотреть документацию Apple по ее набору API Core Foundation. Это чистый API C, но многие типы связаны с эквивалентами объектов Objective-C.

Вам также может быть полезно взглянуть на дизайн самого Objective-C. Он немного отличается от C ++ тем, что объектная система определяется в терминах функций C, например objc_msg_send для вызова метода объекта.Компилятор переводит синтаксис квадратных скобок в эти вызовы функций, поэтому вам не нужно его знать, но, учитывая ваш вопрос, вы можете найти полезным узнать, как он работает под капотом.

10
ответ дан 22 November 2019 в 22:45
поделиться

Проверьте GObject . Он предназначен для объектно-ориентированного программирования на C и является одной из реализаций того, что вы ищете. Если вы действительно хотите объектно-ориентированного программирования, используйте C ++ или другой язык объектно-ориентированного программирования. Если вы привыкли иметь дело с объектно-ориентированными языками, с GObject иногда может быть очень сложно работать, но, как и все остальное, вы привыкнете к соглашениям и потоку.

13
ответ дан 22 November 2019 в 22:45
поделиться

Пространства имен часто создаются следующим образом:

stack_push(thing *)

вместо

stack::push(thing *)

Чтобы преобразовать структуру C в нечто вроде ] Класс 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 подобен статической переменной класса - совместно используется всеми экземплярами типа. Все методы действительно статичны, за исключением того, что некоторые используют this *

83
ответ дан 22 November 2019 в 22:45
поделиться

В каких статьях или книгах можно использовать концепции ООП в C?

Интерфейсы и реализации C Дейва Хэнсона - это отлично по инкапсуляции и именованию и очень хорошо по использованию указателей функций. Дэйв не пытается имитировать наследование.

4
ответ дан 22 November 2019 в 22:45
поделиться

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

Наследование и полиморфизм - другие аспекты, которые часто считаются важными для ООП - не обязательно обеспечивают продуктивность выгоды, которые они обещают, и разумные аргументы , что они действительно могут препятствовать развитию и размышлениям о проблемной области.

17
ответ дан 22 November 2019 в 22:45
поделиться

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

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

10
ответ дан 22 November 2019 в 22:45
поделиться

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

Чтобы ответить на первоначальный вопрос, вот несколько ресурсов, обучающих ООП на C:

Запись блога EmbeddedGurus.com "Объектно-ориентированное программирование на C" показывает, как реализовать классы и одиночное наследование в переносимом C: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/

Application Note ""C+"-Object Oriented Programming in C" показывает, как реализовать классы, одиночное наследование и позднее связывание (полиморфизм) в C с помощью макросов препроцессора: http://www.state-machine.com/resources/cplus_3.0_manual.pdf, код примера доступен по адресу http://www.state-machine.com/resources/cplus_3.0.zip

53
ответ дан 22 November 2019 в 22:45
поделиться

Если вы убеждены, что ООП лучше подходит для проблемы, которую вы пытаетесь решить, зачем вам пытаться решить ее без ООП язык? Похоже, вы используете не тот инструмент для работы. Используйте C ++ или другой объектно-ориентированный вариант языка C.

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

Если после всего этого вы действительно настроены на ООП C, прочтите это (PDF).

7
ответ дан 22 November 2019 в 22:45
поделиться

Одна вещь, которую вы, возможно, захотите сделать, - это изучить реализация инструментария Xt для X Window . Конечно, он становится длинным, но многие из используемых структур были разработаны для работы в ОО-стиле в рамках традиционного языка C. Обычно это означает добавление дополнительного уровня косвенности здесь и там и проектирование структур, которые накладываются друг на друга.

Вы действительно можете многое сделать, используя объектно-ориентированный подход, расположенный в C таким образом, хотя иногда кажется, что объектно-ориентированные концепции не полностью сформировались из разума #include . Они действительно составляли многие из лучших практик того времени. ОО-языки и системы только дистиллировали и расширили часть духа программирования того времени.

4
ответ дан 22 November 2019 в 22:45
поделиться

Я предлагаю использовать Objective-C, который является надмножеством C.

Хотя Objective-C уже 30 лет, он позволяет писать элегантный код.

http://en.wikipedia.org/wiki/Objective-C

1
ответ дан 22 November 2019 в 22:45
поделиться

Я думаю, что первое, что нужно сказать, это то, что (IMHO, по крайней мере) реализация указателей функций в C РЕАЛЬНО трудна для использования. Я бы перепрыгнул через кучу обручей, чтобы избежать указателей функций...

что касается этого, я думаю, что то, что сказали другие люди, довольно хорошо. У вас есть структуры, у вас есть модули, вместо foo->method(a,b,c), вы получаете method(foo,a,b,c) Если у вас более одного типа с методом "method", то вы можете префикснуть его с типом, так FOO_method(foo,a,b,c), как сказали другие. ... при хорошем использовании .h файлов вы можете получить private и public и т.д.

Теперь, есть несколько вещей, которые эта техника вам не даст. Она не даст вам приватные поля данных. Это, я думаю, вы должны сделать с помощью силы воли и хорошей гигиены кодирования... Кроме того, нет простого способа сделать наследование с помощью этой техники.

Это, по крайней мере, легкие части... остальное, я думаю, это ситуация 90/10. 10% выгоды потребуют 90% работы...

0
ответ дан 22 November 2019 в 22:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: