Прочитайте SICP и изучите Scheme и практическую идею абстрактных типов данных . Тогда кодирование на C легко (поскольку с SICP, немного C и немного PHP, Ruby и т. Д.) Ваше мышление будет достаточно широким, и вы поймете, что объектно-ориентированное программирование может быть не лучшим стилем в все дела, но только для каких-то программ). Будьте осторожны с динамическим распределением памяти на C , что, вероятно, является самой сложной частью. Стандарт языка программирования C99 или C11 и его стандартная библиотека C на самом деле довольно бедны (он не знает о TCP или каталогах!), И вам часто понадобятся некоторые внешние библиотеки или интерфейсы (например,POSIX , библиотека libcurl для HTTP-клиента, библиотека libonion для HTTP-сервера, GMPlib для bignums, некоторые библиотеки, такие как libunistring для UTF-8 и т. Д.).
Ваши «объекты» часто находятся в некоторых связанных-C struct, и вы определяете набор функций, работающих с ними. Для коротких или очень простых функций рассмотрите возможность их определения с соответствующими значениями struct, как static inlineв некотором заголовочном файле, foo.hкоторый будет #include-d в другом месте.
Обратите внимание, что объектно-ориентированное программирование - не единственная парадигма программирования . В некоторых случаях целесообразны и другие парадигмы ( функциональное программирование в стиле Ocaml или Haskell или даже Scheme или Commmon Lisp, логическое программирование в стиле Prolog и т. Д. И т. Д. ... Читайте также блог Дж. Питрата о декларативном искусственном интеллекте). См. Книгу Скотта: Прагматика языка программирования.
На самом деле, программист на C или в Ocaml, как правило, не хочет кодировать в стиле объектно-ориентированного программирования. Нет причин заставлять себя думать об объектах, когда это бесполезно.
Вы определите некоторые structи функции, действующие на них (часто через указатели). Вам могут понадобиться несколько теговых объединений (часто structс элементом тега, часто с некоторыми enum, а некоторые unionвнутри), и вам может быть полезно иметь гибкий член массива в конце некоторых из ваших struct-s.
Загляните в исходный код некоторых существующих свободных программ на C (смотрите github & sourceforge,
чтобы найти их). Вероятно, было бы полезно установить и использовать дистрибутив Linux: он сделан почти только из свободного программного обеспечения, у него есть отличные компиляторы бесплатного программного обеспечения C ( GCC , Clang / LLVM ) и инструменты разработки. Смотрите также Advanced Linux Programming, если вы хотите разрабатывать для Linux.
Не забудьте собрать все предупреждения и информацию об отладке, например, особенно на gcc -Wall -Wextra -gэтапах разработки и отладки, и научиться использовать некоторые инструменты, например, valgrind для поиска утечек памяти , gdbотладчик и т. Д. Постарайтесь хорошо понять, что не определено поведение и строго избегать его (помните, что программа может иметь некоторое UB и иногда, кажется, "работает").
Когда вам действительно нужны объектно-ориентированные конструкции (в частности, наследование ), вы можете использовать указатели на связанные структуры и функции. Вы можете иметь свой собственный механизм vtable , каждый «объект» должен начинаться с указателя на указатели на structсодержащую функцию. Вы используете возможность приведения типа указателя к другому типу указателя (и тот факт, что вы можете приводить struct super_stтипы, содержащие те же типы полей, что и те, которые начинают struct sub_stэмулировать наследование). Обратите внимание, что C достаточно для реализации довольно сложных объектных систем, в частности, следуя некоторым соглашениям , как демонстрирует GObject (из GTK / Gnome).
Когда вы действительно нужны затворы , вы часто эмулировать их обратные вызовы , с конвенцией , что каждая функция обратного вызова с использованием передаются как указатель на функцию и некоторые данные клиента (потребляемый указатель на функции , когда она называет это). Вы также можете иметь (условно) свои собственные закрывающие struct-s (содержащие некоторый указатель на функцию и закрытые значения).
Поскольку C - это язык очень низкого уровня, важно определить и задокументировать ваши собственные соглашения (вдохновленные практикой в других программах на C), в частности, об управлении памятью, и, возможно, также некоторые соглашения об именах. Полезно иметь представление об архитектуре набора команд . Не забывайте , что C компилятор может сделать много оптимизаций на свой код (если вы попросите его), так что не все равно слишком много о выполнении микро-оптимизаций вручную, отпуск , что ваш компилятор ( для оптимизации компиляции выпущен програмное обеспечение). Если вы заботитесь о тестировании производительности и производительности, вам следует включить оптимизацию (после отладки вашей программы).gcc -Wall -O2
Не забывайте, что иногда метапрограммирование полезно . Довольно часто большое программное обеспечение, написанное на C, содержит некоторые сценарии или специальные программы для генерации некоторого кода C, используемого где-либо еще (и вы также можете использовать некоторые грязные трюки препроцессора C , например, X-макросы ). Существует несколько полезных генераторов программ на Си (например, yacc или gnu bison для генерации парсеров, gperf для генерации совершенных хеш-функций и т. Д.). В некоторых системах (особенно в Linux и POSIX) вы даже можете сгенерировать некоторый код C во время выполнения в generated-001.cфайле, скомпилировать его в общий объект, выполнив некоторую команду (например gcc -O -Wall -shared -fPIC generated-001.c -o generated-001.so) во время выполнения, динамически загрузить этот общий объект, используя dlopen& получить указатель на функцию из имени, используя dlsym . Я делаю такие трюки в MELT (Lisp-подобном доменном языке, который может быть полезен для вас, так как он позволяет настраивать компилятор GCC ).
Помните о концепциях и методах сбора мусора ( подсчет ссылок часто является методом управления памятью в C, и это, ИМХО, плохая форма сборки мусора, которая плохо справляется с циклическими ссылками ; у вас могут быть слабые указатели, чтобы помочь в этом, но это может быть сложно). В некоторых случаях вы могли бы рассмотреть возможность использования консервативного сборщика мусора Бема .
qux = foo.bar(baz)становятсяqux = Foo_bar(foo, baz).