Прочитайте 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)
.