Supondo que eu precise usar C (sem C ++ ou compiladores orientados a objeto) e não possua alocação dinâmica de memória, quais são algumas técnicas que posso usar para implementar uma classe ou uma boa aproximação de uma classe? É sempre uma boa idéia isolar a "classe" em um arquivo separado? Suponha que possamos pré-alocar a memória assumindo um número fixo de instâncias, ou mesmo definindo a referência a cada objeto como uma constante antes do tempo de compilação. Sinta-se livre para fazer suposições sobre qual conceito de POO eu precisarei implementar (ele variará) e sugerir o melhor método para cada um.
Restrições:
- Eu tenho que usar C e não um OOP porque estou escrevendo o código para um sistema incorporado, e o compilador e a base de código preexistente estão em C.
- Não há alocação dinâmica de memória porque não temos memória suficiente para supor razoavelmente que não ficaremos sem se começarmos a alocá-la dinamicamente.
- Os compiladores com os quais trabalhamos não têm problemas com ponteiros de função
apr_
GLib e os prefixosg_
para criar um namespace) e outros fatores organizadores sem OOP. Se você estiver reestruturando o aplicativo, considere gastar algum tempo tentando criar uma estrutura processual mais sustentável.Respostas:
Isso depende do conjunto de recursos "orientado a objetos" exato que você deseja ter. Se você precisar de coisas como sobrecarga e / ou métodos virtuais, provavelmente precisará incluir ponteiros de função nas estruturas:
Isso permitiria implementar uma classe, "herdando" a classe base e implementando uma função adequada:
É claro que isso exige que você também implemente um construtor, que garanta que o ponteiro da função esteja configurado corretamente. Normalmente, você alocaria memória dinamicamente para a instância, mas também pode permitir que o chamador faça isso:
Se você deseja vários construtores diferentes, precisará "decorar" os nomes das funções, não poderá ter mais de uma
rectangle_new()
função:Aqui está um exemplo básico mostrando o uso:
Espero que isso lhe dê algumas idéias, pelo menos. Para uma estrutura orientada a objetos bem-sucedida e rica em C, consulte a biblioteca GObject da glib .
Observe também que não há nenhuma "classe" explícita modelada acima, cada objeto tem seus próprios ponteiros de método, que são um pouco mais flexíveis do que você normalmente encontraria no C ++. Além disso, custa memória. Você pode evitar isso colocando os ponteiros do método em uma
class
estrutura e inventar uma maneira de cada instância de objeto fazer referência a uma classe.fonte
const ShapeClass *
ouconst void *
como argumentos? Parece que este último pode ser um agradável pouco sobre herança, mas eu posso ver argumentos para os dois lados ...float (*computeArea)(const ShapeClass *shape);
dizer queShapeClass
é um tipo desconhecido.struct
próprias referências, ela requer uma declaração antes de ser definida. Isso é explicado com um exemplo aqui na resposta de Lundin . Modificar o exemplo para incluir a declaração de encaminhamento deve resolver seu problema;typedef struct ShapeClass ShapeClass; struct ShapeClass { float (*computeArea)(const ShapeClass *shape); };
Eu tive que fazer isso uma vez também para uma lição de casa. Eu segui essa abordagem:
Um exemplo simples seria o seguinte:
fonte
typedef struct Queue Queue;
lá.Se você deseja apenas uma classe, use uma matriz de
struct
s como dados dos "objetos" e passe ponteiros para eles para as funções "membro". Você pode usartypedef struct _whatever Whatever
antes de declararstruct _whatever
para ocultar a implementação do código do cliente. Não há diferença entre esse "objeto" e oFILE
objeto de biblioteca padrão C.Se você deseja mais de uma classe com herança e funções virtuais, é comum ter ponteiros para as funções como membros da estrutura ou um ponteiro compartilhado para uma tabela de funções virtuais. A biblioteca GObject usa esse e o truque typedef e é amplamente usada.
Há também um livro sobre técnicas para esta disponível on-line - Programação Orientada a Objetos com ANSI C .
fonte
você pode dar uma olhada no GOBject. é uma biblioteca de SO que oferece uma maneira detalhada de criar um objeto.
http://library.gnome.org/devel/gobject/stable/
fonte
Interfaces e implementações C: Técnicas para criar software reutilizável , David R. Hanson
Este livro faz um excelente trabalho ao abordar sua pergunta. Está na série Addison Wesley Professional Computing.
O paradigma básico é algo como isto:
fonte
Vou dar um exemplo simples de como o POO deve ser feito em C. Sei que esse tópico é de 2009, mas gostaria de acrescentar isso de qualquer maneira.
O conceito básico envolve colocar a 'classe herdada' no topo da estrutura. Dessa forma, o acesso aos 4 primeiros bytes na estrutura também acessa os 4 primeiros bytes na 'classe herdada' (otimizações não-loucas). Agora, quando o ponteiro da estrutura é convertido para a 'classe herdada', a 'classe herdada' pode acessar os 'valores herdados' da mesma maneira que acessaria seus membros normalmente.
Esta e algumas convenções de nomenclatura para construtores, destruidores, funções de alocação e desalocação (eu recomendo init, clean, new, free) irão lhe proporcionar um longo caminho.
Quanto às funções virtuais, use ponteiros de função na estrutura, possivelmente com Class_func (...); invólucro também. Quanto aos modelos (simples), adicione um parâmetro size_t para determinar o tamanho, exija um ponteiro nulo * ou exija um tipo de 'classe' com apenas a funcionalidade de sua preferência. (por exemplo, int GetUUID (Object * self); GetUUID (& p);)
fonte
Use a
struct
para simular os membros de dados de uma classe. Em termos de escopo do método, você pode simular métodos privados colocando os protótipos de função privada no arquivo .c e as funções públicas no arquivo .h.fonte
fonte
No seu caso, a boa aproximação da classe pode ser o ADT . Mas ainda assim não será o mesmo.
fonte
Minha estratégia é:
Alguém vê problemas, buracos, armadilhas potenciais ou benefícios / desvantagens ocultos de qualquer variação dessa abordagem? Se estou reinventando um método de design (e suponho que seja), você pode me indicar o nome dele?
fonte
Veja também esta resposta e esta
É possível. Sempre parece uma boa idéia na época, mas depois se torna um pesadelo de manutenção. Seu código fica cheio de pedaços de código que unem tudo. Um novo programador terá muitos problemas para ler e entender o código se você usar ponteiros de função, uma vez que não será óbvio que funções são chamadas.
A ocultação de dados com funções get / set é fácil de implementar em C, mas pára por aí. Eu já vi várias tentativas disso no ambiente incorporado e, no final, é sempre um problema de manutenção.
Como todos vocês têm problemas de manutenção, eu evito.
fonte
Minha abordagem seria mover as funções associadas a
struct
todas as funções associadas principalmente a um (s) arquivo (s) de origem separado, para que ele possa ser usado "de forma portável".Dependendo do seu compilador, você poderá incluir funções no
struct
, mas essa é uma extensão muito específica do compilador e não tem nada a ver com a última versão do padrão que eu usei rotineiramente :)fonte
O primeiro compilador c ++ foi na verdade um pré-processador que traduziu o código C ++ em C.
Portanto, é muito possível ter classes em C. Você pode tentar desenterrar um pré-processador C ++ antigo e ver que tipo de soluções ele cria.
fonte
cfront
; teve problemas quando exceções foram adicionadas ao C ++ - o tratamento de exceções não é trivial.O GTK é construído inteiramente em C e usa muitos conceitos de POO. Eu li o código fonte do GTK e é bastante impressionante e definitivamente mais fácil de ler. O conceito básico é que cada "classe" é simplesmente uma estrutura e funções estáticas associadas. Todas as funções estáticas aceitam a estrutura "instance" como parâmetro, fazem o que for necessário e retornam resultados, se necessário. Por exemplo, você pode ter uma função "GetPosition (CircleStruct obj)". A função simplesmente vasculharia a estrutura, extrairia os números de posição, provavelmente criaria um novo objeto PositionStruct, colocaria xey no novo PositionStruct e o retornaria. O GTK ainda implementa herança dessa maneira incorporando estruturas dentro de estruturas. bem esperto.
fonte
Você quer métodos virtuais?
Caso contrário, basta definir um conjunto de ponteiros de função na própria estrutura. Se você atribuir todos os ponteiros de função às funções C padrão, poderá chamar funções de C em sintaxe muito semelhante à que você faria em C ++.
Se você deseja ter métodos virtuais, fica mais complicado. Basicamente, você precisará implementar sua própria VTable para cada estrutura e atribuir ponteiros de função à VTable, dependendo de qual função é chamada. Você precisaria de um conjunto de ponteiros de função na estrutura que, por sua vez, chama o ponteiro de função na VTable. Isso é, essencialmente, o que o C ++ faz.
TBH embora ... se você quiser o último, provavelmente está melhor apenas encontrando um compilador C ++, pode usar e recompilar o projeto. Eu nunca entendi a obsessão pelo C ++ não ser utilizável no incorporado. Eu o usei muitas vezes e funciona rápido e não tem problemas de memória. Claro que você precisa ter um pouco mais de cuidado com o que faz, mas não é tão complicado assim.
fonte
C não é uma linguagem OOP, como você aponta corretamente, então não há uma maneira integrada de escrever uma classe verdadeira. Sua melhor aposta é olhar para estruturas e indicadores de função , isso permitirá que você construa uma aproximação de uma classe. No entanto, como C é procedural, você pode considerar escrever um código mais semelhante ao C (ou seja, sem tentar usar classes).
Além disso, se você pode usar C, provavelmente pode usar C ++ e obter classes.
fonte