Leia o SICP e aprenda Scheme, além da idéia prática de tipos de dados abstratos . Então codificar em C é fácil (já que com SICP, um pouco de C e um pouco de PHP, Ruby, etc ... seu pensamento seria bastante amplo e você entenderia que a programação orientada a objetos pode não ser o melhor estilo em todos os casos, mas apenas para algum tipo de programa). Tenha cuidado com a alocação de memória dinâmica C , que provavelmente é a parte mais difícil. O padrão da linguagem de programação C99 ou C11 e sua biblioteca padrão C são na verdade bastante pobres (ele não conhece TCP ou diretórios!), E muitas vezes você precisará de algumas bibliotecas ou interfaces externas (por exemplo,POSIX , libcurl para a biblioteca do cliente HTTP, libonion para a biblioteca do servidor HTTP, GMPlib para bignums, alguma biblioteca como libunistring para UTF-8, etc ...).
Seus "objetos" geralmente estão em C - alguns relacionados struct- e você define o conjunto de funções operando neles. Para funções curtas ou muito simples, considere defini-las, com o relevante struct, como static inlineem algum arquivo de cabeçalho, foo.hpara estar #include-d em outro lugar.
Observe que a programação orientada a objetos não é o único paradigma de programação . Em algumas ocasiões, outros paradigmas valem a pena ( programação funcional à Ocaml ou Haskell ou mesmo Scheme ou Commmon Lisp, programação lógica à Prolog, etc etc ... Leia também o blog de J.Pitrat sobre inteligência artificial declarativa). Veja o livro de Scott: Pragmática da Linguagem de Programação
Na verdade, um programador em C ou Ocaml geralmente não deseja codificar em um estilo de programação orientado a objetos. Não há razão para se forçar a pensar em objetos quando isso não é útil.
Você definirá algumas structe as funções que operam nelas (geralmente através de ponteiros). Você pode precisar de algumas uniões marcadas (geralmente uma structcom um membro de tag, muitas vezes algumas enume algumas unioninternas), e pode ser útil ter um membro de matriz flexível no final de alguns dos seus struct-s.
Olhe dentro do código fonte de alguns softwares livres existentes em C (consulte o github & sourceforge
para encontrar alguns). Provavelmente, a instalação e o uso de uma distribuição Linux seria útil: ela é feita quase apenas de software livre, possui ótimos compiladores C de software livre ( GCC , Clang / LLVM ) e ferramentas de desenvolvimento. Consulte também Programação avançada do Linux se você deseja desenvolver para Linux.
Não se esqueça de compilar com todos os avisos e informações de depuração, por exemplo - gcc -Wall -Wextra -gnotavelmente durante as fases de desenvolvimento e depuração - e aprender a usar algumas ferramentas, por exemplo, valgrind para detectar vazamentos de memória , o gdbdepurador, etc. Lembre-se de entender bem o que é indefinido comportamento e evite-o fortemente (lembre-se de que um programa pode ter algum UB e às vezes parece "funcionar").
Quando você realmente precisa de construções orientadas a objetos (em particular herança ), pode usar ponteiros para estruturas e funções relacionadas. Você poderia ter seu próprio maquinário de tabela , ter cada "objeto" começando com um ponteiro para um structponteiro de função contendo. Você aproveita a capacidade de converter um tipo de ponteiro para outro tipo de ponteiro (e do fato de poder converter a partir de um que struct super_stcontenha os mesmos tipos de campo que aqueles que iniciam um struct sub_stpara emular herança). Observe que C é suficiente para implementar sistemas de objetos bastante sofisticados - em particular seguindo algumas convenções -, como o GObject (do GTK / Gnome) demonstra.
Quando você realmente precisa de fechamentos , geralmente os emula com retornos de chamada , com a convenção de que todas as funções que usam um retorno de chamada passam por um ponteiro de função e por alguns dados do cliente (consumidos pelo ponteiro de função quando chama isso). Você também pode ter (convencionalmente) seu próprio fechamento-como- structs (contendo algum ponteiro de função e os valores fechados).
Como C é uma linguagem de nível muito baixo, é importante definir e documentar suas próprias convenções (inspiradas pela prática em outros programas C), em particular sobre gerenciamento de memória e, provavelmente, algumas convenções de nomenclatura. É útil ter uma idéia sobre a arquitetura do conjunto de instruções . Não se esqueça que um compilador C pode fazer muitas otimizações no seu código (se você pedir), então não se importe muito em fazer micro-otimizações manualmente, deixe isso para o seu compilador ( gcc -Wall -O2para compilação otimizada de Programas). Se você se preocupa com benchmarking e desempenho bruto, deve habilitar otimizações (depois que o programa for depurado).
Não se esqueça que às vezes a metaprogramação é útil . Frequentemente, grandes softwares escritos em C contêm alguns scripts ou programas ad-hoc para gerar algum código C usado em outro lugar (e você também pode executar alguns truques sujos do pré-processador C , por exemplo , macros X ). Existem alguns geradores de programas C úteis (por exemplo, yacc ou gnu bison para gerar analisadores, gperf para gerar funções de hash perfeitas, etc ...). Em alguns sistemas (principalmente Linux e POSIX), você pode até gerar algum código C em tempo de execução no generated-001.carquivo, compilá-lo em um objeto compartilhado executando algum comando (como gcc -O -Wall -shared -fPIC generated-001.c -o generated-001.so) em tempo de execução, carregar dinamicamente esse objeto compartilhado usando dlopen& obtenha um ponteiro de função de um nome usando dlsym . Estou fazendo esses truques no MELT (uma linguagem específica de domínio semelhante ao Lisp que pode ser útil para você, pois permite a personalização do compilador GCC ).
Esteja ciente dos conceitos e técnicas de coleta de lixo (a contagem de referência geralmente é uma técnica para gerenciar a memória em C, e é uma forma ruim de coleta de lixo que não lida bem com referências circulares ; você pode ter indicadores fracos para ajudar nisso, mas pode ser complicado). Em algumas ocasiões, você pode considerar usar o coletor de lixo conservador de Boehm .
qux = foo.bar(baz)se tornamqux = Foo_bar(foo, baz).