Como você implementa uma aula em C?

assumindo que tenho de usar C (sem Compiladores orientados a C++ ou a objectos) e que Não tenho 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" para um arquivo separado? Suponha que podemos pré-ligar 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 OOP conceito eu vou precisar implementar (ele vai variar) e sugerir o melhor método para cada um.

restrições:

  • tenho de usar o C e não um OOP. porque estou a escrever um código para um sistema incorporado, e o compilador e a base de códigos pré-existente está em C.
  • não há alocação dinâmica de memória porque não temos memória suficiente para presumir razoavelmente que não vamos acabar se começarmos a distribuir dinamicamente ele.
  • os compiladores com os quais trabalhamos não têm problemas com a função ponteiros
Author: Ben Gartner, 2009-09-10

19 answers

Isso depende do conjunto de características "orientado a objectos" exacto que queres ter. Se você precisar de coisas como sobrecarga e / ou métodos virtuais, você provavelmente precisa incluir ponteiros de função em estruturas:

typedef struct {
  float (*computeArea)(const ShapeClass *shape);
} ShapeClass;

float shape_computeArea(const ShapeClass *shape)
{
  return shape->computeArea(shape);
}

Isto permitir-lhe-ia implementar uma classe, "herdando" a classe base, e implementando uma função adequada:

typedef struct {
  ShapeClass shape;
  float width, height;
} RectangleClass;

static float rectangle_computeArea(const ShapeClass *shape)
{
  const RectangleClass *rect = (const RectangleClass *) shape;
  return rect->width * rect->height;
}

É claro que isto requer que você também Implemente um construtor, que se certifique de que o ponteiro da função está devidamente configurado. Normalmente você dinamicamente alocar memória por exemplo, mas você pode deixar o chamador fazer isso também:

void rectangle_new(RectangleClass *rect)
{
  rect->width = rect->height = 0.f;
  rect->shape.computeArea = rectangle_computeArea;
}

Se você quiser vários construtores diferentes, você terá que "decorar" os nomes das funções, você não pode ter mais do que uma função rectangle_new():

void rectangle_new_with_lengths(RectangleClass *rect, float width, float height)
{
  rectangle_new(rect);
  rect->width = width;
  rect->height = height;
}

Aqui está um exemplo básico que mostra a utilização:

int main(void)
{
  RectangleClass r1;

  rectangle_new_with_lengths(&r1, 4.f, 5.f);
  printf("rectangle r1's area is %f units square\n", shape_computeArea(&r1));
  return 0;
}
Espero que isto te dê algumas ideias, pelo menos. Para uma estrutura bem sucedida e rica orientada a objetos em C, olhe para a biblioteca GObject de glib.

Note também que não há nenhum explícito "classe" sendo modelado acima, cada objeto tem seus próprios ponteiros de método que é um pouco mais flexível do que você normalmente encontra em C++. Além disso, custa memória. Você poderia se afastar disso, enfiando os ponteiros do método em uma estrutura class, e inventar uma maneira para cada instância de objeto referenciar uma classe.

 70
Author: unwind, 2012-08-10 17:30:20
Tive de o fazer uma vez para fazer os trabalhos de casa. Segui esta abordagem:
  1. Defina os seus dados num estrutura.
  2. Defina os seus membros de funções que tome um ponteiro para a sua estrutura como primeira discussão.
  3. faça isso em um cabeçalho & um C. Cabeçalho para a definição do struct & declarações de funções, c para implementacao.

Um exemplo simples seria este:

/// Queue.h
struct Queue
{
    /// members
}
typedef struct Queue Queue;

void push(Queue* q, int element);
void pop(Queue* q);
// etc.
/// 
 22
Author: erelender, 2015-11-16 19:21:50

Se só quiser uma classe, use um array de {[[0]} s como os dados dos " objectos "e passe-lhes pontos para as funções de" membro". Você pode usar typedef struct _whatever Whatever Antes de declarar struct _whatever para esconder a implementação do Código do cliente. Não há diferença entre esse "objeto" e a biblioteca padrão C FILE objeto.

Se você quer mais de uma classe com funções hereditárias e virtuais, então é comum ter ponteiros para as funções como membros da estrutura, ou um ponteiro compartilhado para um tabela de funções virtuais. A biblioteca GObject usa tanto este como o truque typedef, e é amplamente usado.

Há também um livro sobre técnicas para esta programação on - line - orientada a objetos com ANSI C.
 11
Author: Pete Kirkham, 2009-09-10 08:04:49
Podes dar uma vista de olhos ao GOBject. é uma biblioteca OS que te dá uma forma verbosa de fazer um objecto.

Http://library.gnome.org/devel/gobject/stable/

 7
Author: Alex, 2009-09-10 07:59:06

C Interfaces e Implementações: Técnicas para a Criação de Software Reutilizável, David R. Hanson

Http://www.informit.com/store/product.aspx?isbn=0201498413

Este livro faz um excelente trabalho ao cobrir a sua pergunta. Está na série Addison Wesley Professional Computing. O paradigma básico é algo assim:
/* for data structure foo */

FOO *myfoo;
myfoo = foo_create(...);
foo_something(myfoo, ...);
myfoo = foo_append(myfoo, ...);
foo_delete(myfoo);
 7
Author: Mark Harrison, 2009-09-12 23:08:34

Miro Samek desenvolveu um framework c orientado a objectos para o seu framework de máquina de estado: http://sourceforge.net/projects/qpc e também escreveu um livro sobre isso: http://www.state-machine.com/psicc2/.

 6
Author: Frank Grimm, 2009-09-10 11:53:44

Use a struct para simular os dados de uma classe. Em termos de âmbito do método, pode simular métodos privados colocando os protótipos da funçãoprivada na .C file and the public functions in the .h file.

 4
Author: Taylor Leese, 2009-09-10 07:56:04

Vou dar um exemplo simples de como OOP deve ser feito em C. Eu sei que este thead é de 2009, mas gostaria de adicionar isto de qualquer maneira.

/// Object.h
typedef struct Object {
    uuid_t uuid;
} Object;

int Object_init(Object *self);
uuid_t Object_get_uuid(Object *self);
int Object_clean(Object *self);

/// Person.h
typedef struct Person {
    Object obj;
    char *name;
} Person;

int Person_init(Person *self, char *name);
int Person_greet(Person *self);
int Person_clean(Person *self);

/// Object.c
#include "object.h"

int Object_init(Object *self)
{
    self->uuid = uuid_new();

    return 0;
}
uuid_t Object_get_uuid(Object *self)
{ // Don't actually create getters in C...
    return self->uuid;
}
int Object_clean(Object *self)
{
    uuid_free(self->uuid);

    return 0;
}

/// Person.c
#include "person.h"

int Person_init(Person *self, char *name)
{
    Object_init(&self->obj); // Or just Object_init(&self);
    self->name = strdup(name);

    return 0;
}
int Person_greet(Person *self)
{
    printf("Hello, %s", self->name);

    return 0;
}
int Person_clean(Person *self)
{
    free(self->name);
    Object_clean(self);

    return 0;
}

/// main.c
int main(void)
{
    Person p;

    Person_init(&p, "John");
    Person_greet(&p);
    Object_get_uuid(&p); // Inherited function
    Person_clean(&p);

    return 0;
}
O conceito básico consiste em colocar a "classe herdada" no topo da estrutura. Desta forma, acessar os primeiros 4 bytes na estrutura também acessa os primeiros 4 bytes na "classe herdada" (Asuming non-crazy optimalisations). Agora, quando o ponteiro da estrutura é lançado para a "classe herdada", a "classe herdada" pode acessar o "valores herdados" da mesma forma que teria acesso aos seus membros normalmente. Isto e algumas convenções de nomenclatura para construtores, destruidores, alocação e funções deallocarion (eu recomendo init, clean, new, free) lhe darão um longo caminho.

Quanto às funções virtuais, use ponteiros de função na estrutura, possivelmente com Class_func (...); wrapper too. Quanto aos modelos (simples), adicione um parâmetro size_t para determinar o tamanho, necessite de um ponteiro vazio*, ou requeira um tipo de 'classe' com apenas a funcionalidade com que te preocupas. (por exemplo, int GetUUID (Object *self); GetUUID (&p);)

 4
Author: YoYoYonnY, 2016-05-21 00:15:18

No teu caso, a boa aproximação da classe pode ser o anADT . Mas mesmo assim não será a mesma coisa.

 3
Author: Artem Barger, 2009-09-10 07:54:41
A minha estratégia é:
  • Define todo o código para a classe num ficheiro separado
  • Define todas as interfaces para a classe num ficheiro header separado
  • todas as funções de Membros tomam um "ClassHandle" que está em nome da instância (em vez de O. foo(), chamar foo(oHandle)
  • o construtor é substituído por uma função classinit vazio(ClassHandle h, int x, int y,...) Ou Classhandle ClassInit (int x, int y,...) dependendo da estratégia de alocação de memória
  • todos as variáveis de membro são armazenadas como um membro de uma estrutura estática no arquivo de classe, Encapsulando-o no arquivo, impedindo arquivos externos de acessá-lo
  • os objectos são armazenados numa matriz da estrutura estática acima, com pegas predefinidas (visíveis na interface) ou um limite fixo de objectos que podem ser instanciados
  • Se for útil, a classe pode conter funções públicas que irão circular através do array e chamar as funções de todos os objectos instanciados (RunAll() chama cada um Corre (oHandle)
  • Uma função Deinit (ClassHandle h) liberta a memória atribuída (Índice de matriz) na estratégia dinâmica de atribuição
Alguém vê problemas, buracos, potenciais armadilhas ou benefícios/desvantagens ocultos de qualquer uma das variações desta abordagem? Se estou reinventando um método de design (e assumo que devo ser), você pode me indicar o nome dele?
 3
Author: Ben Gartner, 2009-09-10 08:01:47
Há um livro muito extenso sobre o assunto, que pode valer a pena verificar.

Programação Orientada a objectos em ANSI-C

 3
Author: Ruben Steins, 2009-09-10 08:53:16

Consulte Também esta resposta e esta

É possível. Parece sempre uma boa ideia na altura, mas depois torna-se um pesadelo de manutenção. O teu código fica cheio de pedaços de código que ligam tudo. Um novo programador terá muitos problemas de leitura e compreensão do código se você usar ponteiros de função, uma vez que não será óbvio o que as funções são chamadas.

Os dados escondidos com funções get / set são fáceis de implementar em C mas pára aí. Tenho visto várias tentativas para isso no ambiente embutido e no final é sempre um problema de manutenção.

Já que estão todos prontos, tenho problemas de manutenção.
 3
Author: Gerhard, 2017-05-23 12:18:14
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <uchar.h>

/**
 * Define Shape class
 */
typedef struct Shape Shape;
struct Shape {
    /**
     * Variables header...
     */
    double width, height;

    /**
     * Functions header...
     */
    double (*area)(Shape *shape);
};

/**
 * Functions
 */
double calc(Shape *shape) {
        return shape->width * shape->height;
}

/**
 * Constructor
 */
Shape _Shape() {
    Shape s;

    s.width = 1;
    s.height = 1;

    s.area = calc;

    return s;
}

/********************************************/

int main() {
    Shape s1 = _Shape();
    s1.width = 5.35;
    s1.height = 12.5462;

    printf("Hello World\n\n");

    printf("User.width = %f\n", s1.width);
    printf("User.height = %f\n", s1.height);
    printf("User.area = %f\n\n", s1.area(&s1));

    printf("Made with \xe2\x99\xa5 \n");

    return 0;
};
 3
Author: Pierozi, 2015-01-23 16:54:02
Este livro online pode ajudá-lo a resolver o seu problema.
 3
Author: Jaydeep Dhrangdhariya, 2015-07-23 08:07:10

A minha abordagem seria mover as funções struct e todas primariamente associadas para um(s) ficheiro (S) Fonte (s) separado (s) para que possa ser usado "portavelmente".

, Dependendo do seu compilador, você pode ser capaz de incluir funções para o struct, mas isso é uma muito compilador específico de extensão, e não tem nada a ver com a última versão da norma eu rotineiramente utilizados :)

 2
Author: warren, 2009-09-10 07:52:48

O primeiro compilador C++ Foi na verdade um pré-processador que traduziu o código C++ Para C.

Então é muito possível ter aulas em C. Você pode tentar desenterrar um antigo pré-processador C++ e ver que tipo de soluções ele cria.
 2
Author: Toad, 2009-09-10 07:57:33

GTK é construído inteiramente em C e usa muitos conceitos OOP. Eu li através do código fonte de GTK e é muito 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 de "instância" como um parâmetro, fazem o que for preciso e retornam os resultados se necessário. Por exemplo, você pode ter uma função "GetPosition(CircleStruct obj)". A função iria simplesmente escavar através da estrutura, extrair os números de posição, provavelmente construir um novo objeto PositionStruct, colocar o x e y na nova posição, e devolvê-lo. GTK ainda implementa herança desta forma, incorporando estruturas dentro de estruturas. muito inteligente.

 2
Author: rocketsarefast, 2013-03-31 00:44:18
Queres métodos virtuais?

Se não for assim, basta definir um conjunto de ponteiros de função na própria estrutura. Se você atribuir todos os ponteiros de função para funções C padrão, então você será capaz de chamar funções de C em sintaxe muito semelhante ao que você faria em C++.

Se queres ter métodos virtuais, torna-se mais complicado. Basicamente você vai precisar implementar o seu próprio VTable para cada estrutura e atribuir ponteiros de função para o VTable, dependendo de qual a função é chamada. Você precisaria então de um conjunto de ponteiros de função na própria estrutura que por sua vez chamar o ponteiro de função no VTable. Isto é, essencialmente, o que o C++ faz.

Mas a tuberculose ... se você quiser o último, então você provavelmente está melhor apenas encontrar um compilador C++ que você pode usar e re-compilar o projeto. Eu nunca entendi a obsessão com C++ não ser utilizável no embedded. Eu usei-o muitas vezes e ele funciona é rápido e não tem problemas de memória. Certo tens de ter um pouco mais de cuidado com o que fazes, mas não é assim tão complicado.

 1
Author: Goz, 2009-10-18 06:16:06

C não é uma língua OOP, como o seu ponto correcto, por isso não há uma forma integrada de escrever uma classe verdadeira. A melhor aposta é olhar para as estruturas, e para os ponteiros de função , estas irão permitir-lhe construir uma aproximação de uma classe. No entanto, como C é processual você pode querer considerar escrever mais código C-like (ou seja, sem tentar usar classes).

Também, se você pode usar C, você pode provavelmente usar C++ e obter classes.

 0
Author: Benjamin, 2013-11-26 13:30:46