Quais são algumas maneiras de escrever código orientado a objeto em C? Especialmente no que diz respeito ao polimorfismo.
Veja também esta pergunta Stack Overflow Object-orientação no C .
Quais são algumas maneiras de escrever código orientado a objeto em C? Especialmente no que diz respeito ao polimorfismo.
Veja também esta pergunta Stack Overflow Object-orientação no C .
Respostas:
Sim. De fato, Axel Schreiner fornece seu livro "Programação Orientada a Objetos em ANSI-C" de graça, que cobre o assunto completamente.
fonte
Desde que você está falando sobre polimorfismo, sim, você pode, estávamos fazendo esse tipo de coisa anos antes do C ++ surgir.
Basicamente você usa um
struct
para armazenar os dados e uma lista de indicadores de função para apontar para as funções relevantes para esses dados.Portanto, em uma classe de comunicação, você teria uma chamada de abertura, leitura, gravação e fechamento que seria mantida como quatro ponteiros de função na estrutura, juntamente com os dados de um objeto, algo como:
Obviamente, esses segmentos de código acima estariam realmente em um "construtor", como
rs232Init()
.Quando você 'herda' dessa classe, basta alterar os ponteiros para apontar para suas próprias funções. Todos que chamavam essas funções o faziam através dos ponteiros de função, fornecendo seu polimorfismo:
Mais ou menos como uma tabela manual.
Você pode até ter classes virtuais definindo os ponteiros como NULL - o comportamento seria um pouco diferente do C ++ (um dump principal em tempo de execução em vez de um erro em tempo de compilação).
Aqui está um pedaço de código de exemplo que o demonstra. Primeiro, a estrutura de classe de nível superior:
Então temos as funções para a 'subclasse' do TCP:
E o HTTP também:
E, finalmente, um programa de teste para mostrá-lo em ação:
Isso produz a saída:
para que você possa ver que as diferentes funções estão sendo chamadas, dependendo da subclasse.
fonte
tCommClass
nome seria renomeadotCommVT
e umatCommClass
estrutura só teria campos de dados e um únicotCommVT vt
campo apontando para a "única e única" tabela virtual. Carregar todos os ponteiros com cada instância adiciona uma sobrecarga desnecessária e se assemelha mais a como você faria coisas em JavaScript do que em C ++, IMHO.Os namespaces geralmente são feitos da seguinte maneira:
ao invés de
Para transformar uma estrutura C em algo como uma classe C ++, você pode transformar:
Para dentro
E fazer:
Não fiz o destruidor ou excluí, mas segue o mesmo padrão.
this_is_here_as_an_example_only é como uma variável de classe estática - compartilhada entre todas as instâncias de um tipo. Todos os métodos são realmente estáticos, exceto que alguns fazem isso *
fonte
st->my_type->push(st, thing2);
vez dest->my_type.push(st, thing2);
struct stack_type my_type;
vez destruct stack_type * my_type;
Class
estrutura ? Isso tornaria o OO C mais dinâmico que o C ++. Que tal isso? A propósito, +1.Acredito que, além de ser útil por si só, implementar OOP em C é uma excelente maneira de aprender OOP e entender seu funcionamento interno. A experiência de muitos programadores mostrou que, para usar uma técnica de maneira eficiente e confiável, o programador deve entender como os conceitos subjacentes são finalmente implementados. Emular classes, herança e polimorfismo em C ensina exatamente isso.
Para responder à pergunta original, aqui estão alguns recursos que ensinam como fazer POO em C:
Postagem no blog EmbeddedGurus.com "Programação baseada em objeto em C" mostra como implementar classes e herança única em C portátil: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c /
Nota da aplicação "" C + "- Programação Orientada a Objetos em C" mostra como implementar classes, herança única e ligação tardia (polimorfismo) em C usando macros de pré-processador: http://www.state-machine.com/resources/cplus_3. 0_manual.pdf , o código de exemplo está disponível em http://www.state-machine.com/resources/cplus_3.0.zip
fonte
Eu já vi isso. Eu não recomendaria. O C ++ começou originalmente dessa maneira como um pré-processador que produzia o código C como uma etapa intermediária.
Essencialmente, o que você acaba fazendo é criar uma tabela de despacho para todos os seus métodos nos quais você armazena suas referências de função. A derivação de uma classe implicaria copiar esta tabela de expedição e substituir as entradas que você desejava substituir, com seus novos "métodos" tendo que chamar o método original se desejar invocar o método base. Eventualmente, você acaba reescrevendo C ++.
fonte
glib
escrito em C de maneira objetiva?Claro que isso é possível. É nisso que GObject , a estrutura na qual todo o GTK + e GNOME se baseia, faz.
fonte
A sub-biblioteca C stdio FILE é um excelente exemplo de como criar abstração, encapsulamento e modularidade em C. não adulterado.
Herança e polimorfismo - os outros aspectos muitas vezes considerados essenciais para OOP - não necessariamente fornecer os ganhos de produtividade que eles prometem e razoáveis argumentos têm sido feitos que eles podem realmente prejudicar o desenvolvimento e pensar sobre o domínio do problema.
fonte
Exemplo trivial com um animal e um cachorro: Você reflete o mecanismo de tabela de tabelas do C ++ (de qualquer maneira). Você também separa alocação e instanciação (Animal_Alloc, Animal_New) para não chamarmos malloc () várias vezes. Também devemos passar explicitamente o
this
ponteiro.Se você executasse funções não virtuais, isso é uma coisa rival. Você simplesmente não as adiciona à vtable e as funções estáticas não requerem um
this
ponteiro. A herança múltipla geralmente requer várias vtables para resolver ambiguidades.Além disso, você deve poder usar setjmp / longjmp para manipular exceções.
PS. Isso é testado em um compilador C ++, mas deve ser fácil fazê-lo funcionar em um compilador C.
fonte
typedef
dentro de umstruct
não é possível em C.Confira GObject . Ele deve ser OO em C e uma implementação do que você está procurando. Se você realmente deseja OO, vá com C ++ ou alguma outra linguagem OOP. Às vezes, é muito difícil trabalhar com o GObject se você estiver acostumado a lidar com idiomas OO, mas, como qualquer coisa, você se acostumará às convenções e fluxo.
fonte
Isso foi interessante de ler. Eu mesmo ponderei a mesma pergunta, e os benefícios de pensar nisso são os seguintes:
Tentar imaginar como implementar conceitos OOP em uma linguagem não OOP me ajuda a entender os pontos fortes da linguagem OOp (no meu caso, C ++). Isso me ajuda a avaliar melhor se deve usar C ou C ++ para um determinado tipo de aplicativo - onde os benefícios de um superam o outro.
Ao navegar na web para obter informações e opiniões sobre isso, encontrei um autor que escrevia o código para um processador incorporado e tinha apenas um compilador C disponível: http://www.eetimes.com/discussion/other/4024626/Object-Oriented -C-Criando-Foundation-Classes-Parte-1
No caso dele, analisar e adaptar os conceitos de POO na linguagem C era uma busca válida. Parece que ele estava aberto a sacrificar alguns conceitos de OOP devido à sobrecarga de desempenho resultante da tentativa de implementá-los em C.
A lição que aprendi é que sim, isso pode ser feito até certo ponto, e sim, existem algumas boas razões para tentar.
No final, a máquina está girando os bits do ponteiro da pilha, fazendo o contador do programa pular e calcular as operações de acesso à memória. Do ponto de vista da eficiência, quanto menos desses cálculos forem feitos pelo seu programa, melhor ... mas às vezes temos que pagar esse imposto para que possamos organizar nosso programa de maneira a torná-lo menos suscetível a erros humanos. O compilador de linguagem OOP se esforça para otimizar ambos os aspectos. O programador deve ter muito mais cuidado ao implementar esses conceitos em uma linguagem como C.
fonte
Talvez seja útil consultar a documentação da Apple para o conjunto de APIs da Core Foundation. É uma API C pura, mas muitos dos tipos são conectados a equivalentes de objeto Objective-C.
Você também pode considerar útil o design do próprio Objective-C. É um pouco diferente do C ++, pois o sistema de objetos é definido em termos de funções C, por exemplo,
objc_msg_send
para chamar um método em um objeto. O compilador converte a sintaxe entre colchetes nessas chamadas de função, para que você não precise saber disso, mas, considerando sua pergunta, pode ser útil aprender como ele funciona sob o capô.fonte
Existem várias técnicas que podem ser usadas. O mais importante é mais como dividir o projeto. Usamos uma interface em nosso projeto que é declarada em um arquivo .h e a implementação do objeto em um arquivo .c. A parte importante é que todos os módulos que incluem o arquivo .h veem apenas um objeto como
void *
ae o arquivo .c é o único módulo que conhece os elementos internos da estrutura.Algo assim para uma classe que chamamos de FOO como exemplo:
No arquivo .h
O arquivo de implementação C será algo parecido.
Então, eu dou o ponteiro explicitamente a um objeto para todas as funções desse módulo. Um compilador C ++ faz isso implicitamente, e em C o escrevemos explicitamente.
Eu realmente uso
this
nos meus programas para garantir que meu programa não seja compilado em C ++ e tenha a propriedade de estar em outra cor no meu editor de destaque de sintaxe.Os campos da FOO_struct podem ser modificados em um módulo e outro módulo nem precisa ser recompilado para ainda ser utilizável.
Com esse estilo, eu já manejo grande parte das vantagens do OOP (encapsulamento de dados). Usando ponteiros de função, é ainda fácil implementar algo como herança, mas, honestamente, é realmente raramente útil.
fonte
typedef struct FOO_type FOO_type
vez de um typedef para anular no cabeçalho, obterá o benefício adicional da verificação de tipo, embora ainda não exponha sua estrutura.Você pode fingir usando ponteiros de função e, de fato, acho que é teoricamente possível compilar programas C ++ em C.
Entretanto, raramente faz sentido forçar um paradigma em um idioma, em vez de escolher um idioma que use um paradigma.
fonte
C orientado a objetos, pode ser feito, eu já vi esse tipo de código em produção na Coréia, e foi o monstro mais horrível que eu já vi em anos (foi como no ano passado (2007) que vi o código). Então, sim, isso pode ser feito, e sim, as pessoas já fizeram isso antes, e ainda o fazem até hoje em dia. Mas eu recomendaria C ++ ou Objective-C, ambas são linguagens nascidas de C, com o objetivo de fornecer orientação a objetos com diferentes paradigmas.
fonte
Se você está convencido de que uma abordagem OOP é superior para o problema que você está tentando resolver, por que você tentaria resolvê-la com um idioma que não seja OOP? Parece que você está usando a ferramenta errada para o trabalho. Use C ++ ou alguma outra linguagem variante C orientada a objeto.
Se você está perguntando porque está começando a codificar um projeto grande já existente, escrito em C, não deve tentar forçar seus próprios paradigmas de OOP (ou de qualquer outra pessoa) na infraestrutura do projeto. Siga as diretrizes que já estão presentes no projeto. Em geral, APIs limpas e bibliotecas e módulos isolados irá percorrer um longo caminho para ter um OOP- limpo ish design.
Se, depois de tudo isso, você realmente estiver decidido a fazer OOP C, leia isto (PDF).
fonte
Sim você pode. As pessoas estavam escrevendo C orientado a objetos antes de C ++ ou Objective-C entrar em cena. O C ++ e o Objective-C foram, em partes, tentativas de pegar alguns dos conceitos de OO usados em C e formalizá-los como parte da linguagem.
Aqui está um programa realmente simples que mostra como você pode fazer algo parecido / é uma chamada de método (existem maneiras melhores de fazer isso. Isso é apenas uma prova de que a linguagem suporta os conceitos):
fonte
Obviamente, não será tão bonito quanto usar um idioma com suporte embutido. Eu até escrevi "assembler orientado a objetos".
fonte
Um pequeno código OOC para adicionar:
fonte
Venho cavando isso há um ano:
Como o sistema GObject é difícil de usar com C puro, tentei escrever algumas macros agradáveis para facilitar o estilo OO com C.
Aqui está o site do meu projeto (não tenho tempo suficiente para escrever en. Doc, no entanto, o documento em chinês é muito melhor).
OOC-GCC
fonte
Há um exemplo de herança usando C na palestra de Jim Larson em 1996, apresentada no Seminário de Programação da Hora do Almoço da Seção 312 aqui: C de nível alto e baixo .
fonte
As interfaces e implementações C de Dave Hanson são excelentes em encapsulamento e nomeação e muito boas no uso de ponteiros de função. Dave não tenta simular herança.
fonte
OOP é apenas um paradigma que coloca os dados como mais importantes que o código nos programas. OOP não é um idioma. Assim, como C simples é uma linguagem simples, OOP em C simples também é simples.
fonte
Uma coisa que você pode querer fazer é analisar a implementação do kit de ferramentas Xt para X Window . Claro que está demorando muito tempo no dente, mas muitas das estruturas usadas foram projetadas para funcionar de maneira OO no C. tradicional. Geralmente, isso significa adicionar uma camada extra de indireção aqui e ali e projetar estruturas para se sobreporem.
Você pode realmente fazer muitas coisas no caminho de OO situadas em C dessa maneira, mesmo que pareça que algumas vezes, os conceitos de OO não tenham surgido totalmente da mente de
#include<favorite_OO_Guru.h>
. Eles realmente constituíam muitas das melhores práticas estabelecidas da época. As linguagens e sistemas OO apenas destilam e amplificam partes do zeitgeist de programação do dia.fonte
A resposta para a pergunta é 'Sim, você pode'.
O kit C (OOC) orientado a objetos é para aqueles que desejam programar de maneira orientada a objetos, mas também mantém o bom e velho C. OOC implementa classes, herança única e múltipla, tratamento de exceções.
Recursos
• Utiliza apenas macros e funções C, sem necessidade de extensões de idioma! (ANSI-C)
• Código-fonte fácil de ler para o seu aplicativo. Foi tomado cuidado para tornar as coisas o mais simples possível.
• Herança única de classes
• Herança múltipla por interfaces e mixins (desde a versão 1.3)
• Implementando exceções (em puro C!)
• Funções virtuais para aulas
• Ferramenta externa para fácil implementação de classe
Para mais detalhes, visite http://ooc-coding.sourceforge.net/ .
fonte
Parece que as pessoas estão tentando emular o estilo C ++ usando C. Minha opinião é que fazer programação orientada a objetos C é realmente fazer programação orientada a estruturas. No entanto, você pode obter coisas como ligação tardia, encapsulamento e herança. Para herança, você define explicitamente um ponteiro para as estruturas básicas em sua subestrutura e esta é obviamente uma forma de herança múltipla. Você também precisará determinar se o seu
ajuntar com
c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj
.Portanto, o conselho é seguir um estilo C puro e não tentar forçar um estilo C ++. Além disso, dessa maneira se presta a uma maneira muito limpa de criar uma API.
fonte
Consulte http://slkpg.byethost7.com/instance.html para mais uma reviravolta no OOP em C. Ele enfatiza os dados da instância para reentrada usando apenas o C. nativo. A herança múltipla é feita manualmente usando wrappers de função. A segurança do tipo é mantida. Aqui está uma pequena amostra:
fonte
Estou um pouco atrasado para a festa, mas quero compartilhar minha experiência sobre o assunto: hoje trabalho com coisas incorporadas e o único compilador (confiável) que tenho é C, para que eu queira aplicar a orientação a objetos abordagem em meus projetos incorporados escritos em C.
A maioria das soluções que eu vi até agora usa tipografias pesadamente, por isso perdemos a segurança de tipos: o compilador não ajudará se você cometer um erro. Isso é completamente inaceitável.
Requisitos que tenho:
Expliquei minha abordagem em detalhes neste artigo: Programação orientada a objetos em C ; além disso, existe um utilitário para geração automática de código padrão para classes base e derivadas.
fonte
Construí uma pequena biblioteca onde tentei isso e para mim funciona muito bem. Então, pensei em compartilhar a experiência.
https://github.com/thomasfuhringer/oxygen
A herança única pode ser implementada facilmente usando uma estrutura e estendendo-a para todas as outras classes filho. Uma conversão simples para a estrutura pai torna possível usar métodos pai em todos os descendentes. Desde que você saiba que uma variável aponta para uma estrutura que contém esse tipo de objeto, você sempre pode converter para a classe raiz e fazer introspecção.
Como foi mencionado, os métodos virtuais são um pouco mais complicados. Mas eles são factíveis. Para simplificar, eu apenas uso uma matriz de funções na estrutura de descrição de classe que cada classe filho copia e repovoa slots individuais, quando necessário.
A herança múltipla seria bastante complicada de implementar e tem um impacto significativo no desempenho. Então eu deixo. Considero desejável e útil, em alguns casos, modelar de maneira limpa as circunstâncias da vida real, mas provavelmente 90% dos casos a herança única cobre as necessidades. E a herança única é simples e não custa nada.
Também não me importo com a segurança do tipo. Eu acho que você não deve depender do compilador para evitar erros de programação. E protege você apenas de uma parte bastante pequena dos erros de qualquer maneira.
Normalmente, em um ambiente orientado a objetos, você também deseja implementar a contagem de referências para automatizar o gerenciamento de memória na medida do possível. Então, eu também coloquei uma contagem de referência na classe raiz "Object" e algumas funcionalidades para encapsular a alocação e desalocação da memória heap.
É tudo muito simples e enxuto e me fornece o essencial do OO sem me forçar a lidar com o monstro que é C ++. E mantenho a flexibilidade de permanecer na terra C, o que, entre outras coisas, facilita a integração de bibliotecas de terceiros.
fonte
Proponho usar o Objective-C, que é um superconjunto de C.
Embora o Objective-C tenha 30 anos, ele permite escrever um código elegante.
http://en.wikipedia.org/wiki/Objective-C
fonte
Sim, mas nunca vi alguém tentar implementar qualquer tipo de polimorfismo com C.
fonte