Объектно-ориентированное программирование на C [дубликат]




Возможные Дубликаты:
можете ли вы написать объектно-ориентированный код на C?
объектно-ориентированный шаблон в C ?






Я помню, как читал некоторое время назад о ком-то (я думаю, что это был Линус Торвальдс), говорящий о том, как C++ - ужасный язык и как вы можете писать объектно-ориентированные программы с C. После того, как у меня было время подумать, я действительно не вижу, как все объектно-ориентированные концепции переносятся в C. Некоторые вещи довольно очевидный. Например:




  1. эмулировать функции-члены, вы можете поставить указатели на функции в структурах.

  2. чтобы эмулировать полиморфизм, вы можете написать функцию, которая принимает переменное количество аргументов и делает некоторые вуду в зависимости, скажем, от sizeof параметр(ы)


как бы вы эмулировали инкапсуляцию и наследование?



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

607   13  

13 ответов:

вы можете реализовать полиморфизм с помощью регулярных функций и виртуальных таблиц (vtables). Вот довольно аккуратная система, которую я изобрел (на основе C++) для упражнения по программированию:alt text

конструкторы выделяют память, а затем вызывают функцию init класса, где инициализируется память. Каждая функция init должна также содержать статическую структуру vtable, содержащую указатели на виртуальные функции (NULL для чисто виртуальных). Производные функции инициализации класса вызывают суперкласс init функция, прежде чем делать что-либо еще.

очень хороший API может быть создан путем реализации виртуальных оболочек функций (не путать с функциями, на которые указывают vtables) следующим образом (добавить static inline перед ним, если вы делаете это в заголовок):

int playerGuess(Player* this) { return this->vtable->guess(this); }

одно наследование может быть сделано путем злоупотребления двоичным макетом структуры:alt text

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

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

инкапсуляция было сделано путем определения структур в player_protected.h и реализация функций (на которые указывает vtable) в player_protected.c, и аналогично для производных классов, но это довольно неуклюже и ухудшает производительность (поскольку виртуальные оболочки не могут быть помещены в заголовки), поэтому я бы рекомендовал против этого.

вы читали "Библию" на эту тему? Смотрите Объектно-Ориентированный C...

Как бы вы эмулировали инкапсуляцию и наследование?

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

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

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

в ответ на Neil see Википедия для объяснения того, почему наследование не является необходимым для полиморфизма.

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

платформа CoreFoundation от Apple на основе C была фактически написана так, чтобы ее "объекты" могли удвоиться как объекты в Objective-C, фактическом языке OO. Довольно большая часть рамок с открытым исходным кодом на сайте Apple как CF-Lite. Возможно, это будет полезное тематическое исследование в основной структуре уровня ОС, выполненной таким образом.

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

struct T
{
   int data;
   int get_data() const { return data; }
};

Я бы предложил поближе взглянуть на GTK + объект и тип Система. Это блестящий пример ООП реализованной на языке программирования C:

GTK + реализует собственный пользовательский объект система, которая предлагает стандарт объектно-ориентированные функции, такие как наследование и виртуальная функция

ассоциация также может быть договорной и обычной.

Что касается инкапсуляции и методов сокрытия данных, популярным и простым может быть Непрозрачный Указатель (или непрозрачный тип данных) - вы может передавать его, но для того, чтобы загрузить или сохранить любую информацию, вы должны вызвать связанную функцию, которая знает, как разговаривать с объектом, скрытым за этим непрозрачным указателем.

еще один, похожий, но другой теневой тип данных - проверить эту ссылку, где Джон Джаггер дает отличное объяснение этой не очень хорошо известной техники.

библиотеки gtk и glib используют макросы для приведения объектов к различным типам.
add_widget (GTK_WIDGET (myButton));
Я не могу сказать, как это делается, но вы можете прочитать их источник, чтобы точно узнать, как это делается.

взгляните на то, как работает слой VFS в ядре Linux для примера шаблона наследования. Файловые операции для различных файловых систем "наследуют" набор общих функций файловых операций (например generic_file_aio_read(),generic_file_llseek()...), но может переопределить их с помощью собственных реализаций (например. ntfs_file_aio_write()).

определенно посмотрите на Objective-C.

typedef struct objc_object {
    Class isa;
} *id;

typedef struct objc_class {
    struct objc_class *isa;
    struct objc_class *super_class
    const char *name;
    long version;
    long info
    long instance_size;
    struct objc_ivar_list *ivars;
    struct objc_method_list **methodLists;
   struct objc_cache *cache;
   struct objc_protocol_list *protocols;
} *Class;

Как вы можете видеть, информация о наследовании вместе с другими деталями хранится в структуре класса (удобно, что класс также можно рассматривать как объект).

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

однажды я написал базовую программу OO style C paint как часть графического курса - я не пошел так далеко, как объявление класса, я просто использовал указатель vtable в качестве первого элемента структуры и реализовал ручное наследование. Хорошая вещь об игре с vtables на таком низком уровне заключается в том, что вы можете изменить поведение класса во время выполнения, изменив несколько указателей или динамически изменив класс объектов. Было довольно легко создать все виды гибридных объектов, поддельное множественное наследование и т. д.

хорошая статья и обсуждение относительно Objective-C здесь:

http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html

для отличного примера объектно-ориентированного программирования в C, посмотрите на источник POV-Ray от нескольких лет назад-версия 3.1 g особенно хороша. "Объекты" были структурированы с указателями на функции, конечно. Макросы использовались для предоставления основных методов и данных для абстрактного объекта, а производные классы были структурами, которые начинались с этого макроса. Однако не было никаких попыток иметь дело с частным / государственным. Дел не видно было .H-файлы и подробности осуществления .файлы c, в основном, за исключением многих исключений.

были некоторые аккуратные трюки, которые я не вижу, как можно перенести на C++ - например, преобразование одного класса в другой, но похожий на лету, просто переназначая указатели функций. Простой для современных динамических языков. Я забыл детали; я думаю, что это могли быть объекты CSG intersection и union.

http://www.povray.org/

интересная история. Cfront, исходная реализация C++ выводит код C, а затем требует компилятора C для фактического построения окончательного кода. Итак, все, что может быть выражено в C++, может быть записано как C.

один из способов обработки наследования является наличие вложенных структур:

struct base
{
    ...
};

void method_of_base(base *b, ...);

struct child
{
    struct base base_elements;
    ...
};

затем вы можете делать звонки, так:

struct child c;
method_of_base(&c.b, ...);

вы можете посмотреть на Objective-C, это в значительной степени то, что он делает. Это просто интерфейс, который компилирует код Objective-C OO в C.

Comments

    Ничего не найдено.