Pouvez-vous écrire du code orienté objet en C? [fermé]

pouvez-vous écrire du code orienté objet en C? En particulier à l'égard de polymorphisme.


Voir aussi la question sur le débordement de la cheminée objet-orientation dans C .

429
demandé sur Peter Mortensen 2008-12-09 07:02:40
la source

30 ответов

Oui. En fait Axel Schreiner fournit son livre " la programmation orientée objet en ANSI-C " GRATUITEMENT qui couvre le sujet assez complètement.

319
répondu mepcotterell 2015-12-31 18:52:13
la source

Puisque vous parlez de polymorphisme alors oui, vous pouvez, nous faisions ce genre de trucs années avant le C++.

fondamentalement, vous utilisez un struct pour contenir à la fois les données et une liste de pointeurs de fonction pour pointer vers les fonctions pertinentes pour ces données.

ainsi, dans une classe de communication, vous auriez un appel ouvert, lire, écrire et fermer qui serait maintenu comme quatre pointeurs de fonction dans la structure, à côté de la données pour un objet, quelque chose comme:

typedef struct {
    int (*open)(void *self, char *fspec);
    int (*close)(void *self);
    int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
    // And data goes here.
} tCommClass;

tCommClass commRs232;
commRs232.open = &rs232Open;
: :
commRs232.write = &rs232Write;

tCommClass commTcp;
commTcp.open = &tcpOpen;
: :
commTcp.write = &tcpWrite;

bien sûr, les segments de code ci-dessus seraient en fait dans un" constructeur "tel que rs232Init() .

quand vous "héritez" de cette classe, vous changez simplement les pointeurs pour pointer vers vos propres fonctions. Tous ceux qui ont appelé ces fonctions le feraient à travers les pointeurs de fonction, vous donnant votre polymorphisme:

int stat = (commTcp.open)(commTcp, "bigiron.box.com:5000");

un peu comme une table manuelle.

vous pouvez même avoir des classes virtuelles en paramétrant les pointeurs à NULL-le comportement serait légèrement différent de C++ (un dump du noyau au moment de l'exécution plutôt qu'une erreur au moment de la compilation).

Voici un exemple de code qui le démontre. D'abord la structure de classe de haut niveau:

#include <stdio.h>

// The top-level class.

typedef struct sCommClass {
    int (*open)(struct sCommClass *self, char *fspec);
} tCommClass;

alors nous avons les fonctions pour la 'sous-classe' TCP:

// Function for the TCP 'class'.

static int tcpOpen (tCommClass *tcp, char *fspec) {
    printf ("Opening TCP: %s\n", fspec);
    return 0;
}
static int tcpInit (tCommClass *tcp) {
    tcp->open = &tcpOpen;
    return 0;
}

et le HTTP:

// Function for the HTTP 'class'.

static int httpOpen (tCommClass *http, char *fspec) {
    printf ("Opening HTTP: %s\n", fspec);
    return 0;
}
static int httpInit (tCommClass *http) {
    http->open = &httpOpen;
    return 0;
}

et enfin un programme d'essai pour le montrer en action:

// Test program.

int main (void) {
    int status;
    tCommClass commTcp, commHttp;

    // Same 'base' class but initialised to different sub-classes.

    tcpInit (&commTcp);
    httpInit (&commHttp);

    // Called in exactly the same manner.

    status = (commTcp.open)(&commTcp, "bigiron.box.com:5000");
    status = (commHttp.open)(&commHttp, "http://www.microsoft.com");

    return 0;
}

produit la sortie:

Opening TCP: bigiron.box.com:5000
Opening HTTP: http://www.microsoft.com

de sorte que vous pouvez voir que les différentes fonctions sont appelés, selon la sous-classe.

297
répondu paxdiablo 2017-05-12 03:39:22
la source

les espaces de noms sont souvent fait en faisant:

stack_push(thing *)

au lieu de

stack::push(thing *)

pour faire un c structure en quelque chose comme un c++ classe que vous pouvez tourner:

class stack {
     public:
        stack();
        void push(thing *);
        thing * pop();
        static int this_is_here_as_an_example_only;
     private:
        ...
};

en

struct stack {
     struct stack_type * my_type;
     // Put the stuff that you put after private: here
};
struct stack_type {
     void (* construct)(struct stack * this); // This takes uninitialized memory
     struct stack * (* operator_new)(); // This allocates a new struct, passes it to construct, and then returns it
     void (*push)(struct stack * this, thing * t); // Pushing t onto this stack
     thing * (*pop)(struct stack * this); // Pops the top thing off the stack and returns it
     int this_is_here_as_an_example_only;
}Stack = {
    .construct = stack_construct,
    .operator_new = stack_operator_new,
    .push = stack_push,
    .pop = stack_pop
};
// All of these functions are assumed to be defined somewhere else

et do:

struct stack * st = Stack.operator_new(); // Make a new stack
if (!st) {
   // Do something about it
} else {
   // You can use the stack
   stack_push(st, thing0); // This is a non-virtual call
   Stack.push(st, thing1); // This is like casting *st to a Stack (which it already is) and doing the push
   st->my_type.push(st, thing2); // This is a virtual call
}

je n'ai pas fait le destructeur ou supprimer, mais il suit le même schéma.

this_is_here_as_an_example_only est comme une classe statique variable -- partagée entre toutes les instances d'un type. Toutes les méthodes sont vraiment statiques, sauf que certaines prennent ceci * 151960920"

78
répondu nategoose 2015-12-31 19:14:13
la source

je crois qu'en plus d'être utile en soi, la mise en œuvre de L'OOP en C est un excellent moyen de apprendre OOP et de comprendre son fonctionnement interne. L'expérience de nombreux programmeurs a montré que pour utiliser une technique efficacement et en toute confiance, un programmeur doit comprendre comment les concepts sous-jacents sont finalement mis en œuvre. L'émulation des classes, de l'héritage et du polymorphisme en C enseigne justement ceci.

pour répondre à la question initiale, voici quelques ressources qui enseignent comment faire de L'OOP en C:

EmbeddedGurus.com blog post "la programmation basée sur L'objet en C" montre comment mettre en œuvre les classes et l'héritage unique en C portable: http://embeddedgurus.com/state-space/2008/01/object-based-programming-in-c/

Note d'Application ""C+"-Programmation Orientée Objet en C", montre comment mettre en œuvre les classes, l'héritage simple, et la liaison tardive (polymorphisme) en C à l'aide les macros du préprocesseur: http://www.state-machine.com/resources/cplus_3.0_manual.pdf , le code exemple est disponible à partir de http://www.state-machine.com/resources/cplus_3.0.zip

49
répondu Miro 2010-04-29 05:37:14
la source

je l'ai vu faire. Je ne le recommande pas. À l'origine, C++ était un préprocesseur qui produisait du code C comme étape intermédiaire.

essentiellement ce que vous finissez par faire est de créer une table d'expédition pour toutes vos méthodes où vous stockez vos références de fonction. Dériver une classe impliquerait de copier cette table de répartition et de remplacer les entrées que vous vouliez outrepasser, avec vos nouvelles "méthodes" devant appeler la méthode originale si elle veut invoquer la méthode de base. Finalement, vous finissez par réécrire C++.

30
répondu tvanfosson 2008-12-09 07:04:02
la source

bien sûr que c'est possible. C'est ce que fait GObject , le cadre sur lequel est basé GTK+ et GNOME .

25
répondu Peter Mortensen 2015-12-31 18:57:01
la source

Le C stdio FICHIER de sous-bibliothèque est un excellent exemple de la façon de créer de l'abstraction, l'encapsulation et la modularité dans la pure C.

héritage et polymorphisme - les autres aspects souvent considérés comme essentiels à la pop - ne fournissent pas nécessairement les gains de productivité qu'ils promettent et raisonnable arguments ont ont été faites qu'ils peuvent réellement entraver le développement et la pensée sur le domaine du problème.

17
répondu msw 2016-10-24 14:58:49
la source

exemple Trivial avec un Animal et le Chien: Vous miroir C++'vtable mécanisme (en grande partie en tout cas). Vous séparez aussi l'allocation et l'instantiation (Animal_Alloc, Animal_New) donc nous n'appelons pas malloc() plusieurs fois. Nous devons aussi passer explicitement le pointeur this .

si vous deviez faire des fonctions non-virtuelles, c'est trivalent. Vous ne les ajoutez tout simplement pas aux fonctions vtable et static ne nécessite pas un pointeur this . Héritage Multiple en général nécessite plusieurs vtables pour résoudre les ambiguïtés.

aussi, vous devriez être capable d'utiliser setjmp/longjmp pour faire la manipulation d'exception.

struct Animal_Vtable{
    typedef void (*Walk_Fun)(struct Animal *a_This);
    typedef struct Animal * (*Dtor_Fun)(struct Animal *a_This);

    Walk_Fun Walk;
    Dtor_Fun Dtor;
};

struct Animal{
    Animal_Vtable vtable;

    char *Name;
};

struct Dog{
    Animal_Vtable vtable;

    char *Name; // Mirror member variables for easy access
    char *Type;
};

void Animal_Walk(struct Animal *a_This){
    printf("Animal (%s) walking\n", a_This->Name);
}

struct Animal* Animal_Dtor(struct Animal *a_This){
    printf("animal::dtor\n");
    return a_This;
}

Animal *Animal_Alloc(){
    return (Animal*)malloc(sizeof(Animal));
}

Animal *Animal_New(Animal *a_Animal){
    a_Animal->vtable.Walk = Animal_Walk;
    a_Animal->vtable.Dtor = Animal_Dtor;
    a_Animal->Name = "Anonymous";
    return a_Animal;
}

void Animal_Free(Animal *a_This){
    a_This->vtable.Dtor(a_This);

    free(a_This);
}

void Dog_Walk(struct Dog *a_This){
    printf("Dog walking %s (%s)\n", a_This->Type, a_This->Name);
}

Dog* Dog_Dtor(struct Dog *a_This){
    // Explicit call to parent destructor
    Animal_Dtor((Animal*)a_This);

    printf("dog::dtor\n");

    return a_This;
}

Dog *Dog_Alloc(){
    return (Dog*)malloc(sizeof(Dog));
}

Dog *Dog_New(Dog *a_Dog){
    // Explict call to parent constructor
    Animal_New((Animal*)a_Dog);

    a_Dog->Type = "Dog type";
    a_Dog->vtable.Walk = (Animal_Vtable::Walk_Fun) Dog_Walk;
    a_Dog->vtable.Dtor = (Animal_Vtable::Dtor_Fun) Dog_Dtor;

    return a_Dog;
}

int main(int argc, char **argv){
    /*
      Base class:

        Animal *a_Animal = Animal_New(Animal_Alloc());
    */
    Animal *a_Animal = (Animal*)Dog_New(Dog_Alloc());

    a_Animal->vtable.Walk(a_Animal);

    Animal_Free(a_Animal);
}

PS. C'est testé sur un compilateur C++, mais il devrait être facile de le faire fonctionner sur un compilateur C.

15
répondu Jasper Bekkers 2015-12-31 19:02:51
la source

Check out GObject . Il est destiné à être OO en C et une mise en œuvre de ce que vous recherchez. Si vous voulez vraiment OO cependant, allez avec C++ ou un autre langage OOP. GObject peut être vraiment difficile de travailler avec parfois si vous avez l'habitude de traiter avec les langues OO, mais comme tout, vous vous habituerez aux conventions et au flux.

12
répondu NG. 2010-04-28 23:53:40
la source

Cela a été intéressant à lire. J'ai moi-même réfléchi à la même question, et les avantages d'y réfléchir sont les suivants:

  • essayer d'imaginer comment implémenter des concepts OOP dans un langage non-OOP m'aide à comprendre les forces du langage OOp (dans mon cas, C++). Cela m'aide à mieux juger s'il faut utiliser C ou C++ pour un type d'application donné -- où les avantages de l'un l'emportent sur les avantages de l'autre.

  • dans ma navigation sur le web pour des informations et des opinions à ce sujet, j'ai trouvé un auteur qui écrivait le code pour un processeur intégré et avait seulement un compilateur C disponible: http://www.eetimes.com/discussion/other/4024626/Object-Oriented-C-Creating-Foundation-Classes-Part-1

dans son cas, l'analyse et l'adaptation des concepts de L'OOP en C simple était une poursuite valide. Il semble qu'il a été ouvert pour sacrifier certains concepts OOP en raison de la performance overhead hit résultant de tenter de les mettre en œuvre en C.

la leçon que j'ai apprise est que, oui, on peut le faire à un certain degré, et oui, il y a de bonnes raisons de le faire.

en fin de compte, la machine est en train de tourner des bits pointeurs de pile, ce qui fait que le programme contre-saute et calcule les opérations d'accès à la mémoire. Du point de vue de l'efficacité, le moins de ces calculs effectués par votre programme, le mieux... mais parfois, nous devons payer cette taxe simplement pour que nous puissions organiser notre programme d'une manière qui le rend le moins susceptible à l'erreur humaine. Le compilateur de langage OOP s'efforce d'optimiser ces deux aspects. Le programmeur doit être beaucoup plus prudent en mettant en œuvre ces concepts dans un langage comme C.

12
répondu RJB 2015-12-31 19:28:40
la source

vous pouvez trouver utile de regarder la documentation D'Apple pour son ensemble de base D'API. Il s'agit d'une API C pure, mais plusieurs de ces types sont reliés à des équivalents objectifs-C.

vous pouvez également trouver utile d'examiner la conception de L'Objectif-C lui-même. C'est un peu différent de C++ en ce que le système objet est défini en termes de fonctions C, par exemple objc_msg_send pour appeler une méthode sur un objet. Le compilateur traduit la syntaxe des crochets en ces appels de fonction, donc vous n'avez pas à le savoir, mais en considérant votre question vous pouvez trouver utile d'apprendre comment il fonctionne sous le capot.

10
répondu benzado 2010-04-28 23:58:36
la source

plusieurs techniques peuvent être utilisées. Le plus important est de savoir comment partager le projet. Nous utilisons une interface dans notre projet, qui est déclaré dans un .h et la mise en œuvre de l'objet dans A.c fichier. La partie importante est que tous les modules qui comprennent le .h fichier voir seulement un objet comme un void * , et le .le fichier c est le seul module qui connaît les internes de la structure.

quelque chose comme ça pour une classe que nous appelons FOO exemple:

dans le .h fichier

#ifndef FOO_H_
#define FOO_H_

...
 typedef struct FOO_type FOO_type;     /* That's all the rest of the program knows about FOO */

/* Declaration of accessors, functions */
FOO_type *FOO_new(void);
void FOO_free(FOO_type *this);
...
void FOO_dosomething(FOO_type *this, param ...):
char *FOO_getName(FOO_type *this, etc);
#endif

le fichier d'implémentation C ressemblera à ça.

#include <stdlib.h>
...
#include "FOO.h"

struct FOO_type {
    whatever...
};


FOO_type *FOO_new(void)
{
    FOO_type *this = calloc(1, sizeof (FOO_type));

    ...
    FOO_dosomething(this, );
    return this;
}

donc je donne le pointeur explicitement à un objet à chaque fonction de ce module. Un compilateur C++ le fait implicitement, et en C nous l'écrivons explicitement.

j'utilise vraiment this dans mes programmes, pour m'assurer que mon programme ne se compile pas en C++, et il a la propriété fine de être dans une autre couleur dans ma coloration syntaxique de l'éditeur.

les champs du FOO_struct peuvent être modifiés dans un module et un autre module n'a même pas besoin d'être recompilé pour être encore utilisable.

avec ce style je gère déjà une grande partie des avantages de OOP (data encapsulation). En utilisant des pointeurs de fonction, il est même facile d'implémenter quelque chose comme l'héritage, mais honnêtement, c'est seulement rarement utile.

10
répondu Patrick Schlüter 2015-12-31 19:12:55
la source

orienté Objet C, qui peut être fait, j'ai vu que le type de code de production en Corée, et c'était le plus horrible monstre que j'avais vu depuis des années (c'était l'année dernière(2007) que j'ai vu le code). Donc oui, cela peut être fait, et oui les gens l'ont déjà fait, et le font encore aujourd'hui. Mais je recommande le C++ ou L'Objectif-C, Tous les deux sont des langues nées du C, avec le but de fournir une orientation objet avec des paradigmes différents.

7
répondu Robert Gould 2008-12-09 07:09:17
la source

si vous êtes convaincu qu'une approche OOP est supérieure pour le problème que vous essayez de résoudre, pourquoi voudriez-vous essayer de le résoudre avec un langage non-OOP? On dirait que vous utilisez le mauvais outil pour le travail. Utilisez C++ ou un autre langage de variante C orienté objet.

si vous demandez parce que vous commencez à coder sur un grand projet existant déjà écrit en C, alors vous ne devriez pas essayer de forcer votre propre (ou n'importe qui d'autre) OOP paradigmes dans le infrastructure du projet. Suivez les directives qui sont déjà présentes dans le projet. En général, les API propres et les bibliothèques et modules isolés iront un long chemin vers avoir un OOP propre - ish conception.

Si, après tout cela, vous êtes vraiment décidé à faire de la POO en C, lire ce (PDF).

7
répondu RarrRarrRarr 2010-04-28 23:49:02
la source

vous pouvez le simuler en utilisant des pointeurs de fonction, et en fait, je pense qu'il est théoriquement possible de compiler des programmes C++ en C.

cependant, il est rarement logique de forcer un paradigme sur une langue plutôt que de choisir une langue qui utilise un paradigme.

6
répondu Uri 2008-12-09 07:05:56
la source

Oui, vous pouvez. Les gens écrivaient C orienté objet avant C++ ou objectif-c est arrivé sur la scène. C++ et Objective-C ont été, en partie, des tentatives de reprendre certains des concepts D'OO utilisés dans C et de les formaliser en tant que partie du langage.

voici un programme vraiment simple qui montre comment vous pouvez faire quelque chose qui ressemble à/est un appel de méthode (il y a de meilleures façons de le faire. Ce n'est que la preuve que le langage supporte les concepts):

#include<stdio.h>

struct foobarbaz{
    int one;
    int two;
    int three;
    int (*exampleMethod)(int, int);
};

int addTwoNumbers(int a, int b){
    return a+b;
}

int main()
{
    // Define the function pointer
    int (*pointerToFunction)(int, int) = addTwoNumbers;

    // Let's make sure we can call the pointer
    int test = (*pointerToFunction)(12,12);
    printf ("test: %u \n",  test);

    // Now, define an instance of our struct
    // and add some default values.
    struct foobarbaz fbb;
    fbb.one   = 1;
    fbb.two   = 2;
    fbb.three = 3;

    // Now add a "method"
    fbb.exampleMethod = addTwoNumbers;

    // Try calling the method
    int test2 = fbb.exampleMethod(13,36);
    printf ("test2: %u \n",  test2);

    printf("\nDone\n");
    return 0;
}
6
répondu Alan Storm 2015-12-31 19:00:07
la source

bien sûr, ce ne sera pas aussi joli qu'utiliser un langage avec support intégré. J'ai même écrit "assembleur d'objets".

6
répondu Darron 2015-12-31 19:03:30
la source

un petit code OOC à ajouter:

#include <stdio.h>

struct Node {
    int somevar;
};

void print() {
    printf("Hello from an object-oriented C method!");
};

struct Tree {
    struct Node * NIL;
    void (*FPprint)(void);
    struct Node *root;
    struct Node NIL_t;
} TreeA = {&TreeA.NIL_t,print};

int main()
{
    struct Tree TreeB;
    TreeB = TreeA;
    TreeB.FPprint();
    return 0;
}
6
répondu 2 revs, 2 users 77%user922475 2015-12-31 19:29:51
la source

je creuse depuis un an:

comme le système GObject est difficile à utiliser avec du C pur, j'ai essayé d'écrire quelques bonnes macros pour faciliter le style OO avec C.

#include "OOStd.h"

CLASS(Animal) {
    char *name;
    STATIC(Animal);
    vFn talk;
};
static int Animal_load(Animal *THIS,void *name) {
    THIS->name = name;
    return 0;
}
ASM(Animal, Animal_load, NULL, NULL, NULL)

CLASS_EX(Cat,Animal) {
    STATIC_EX(Cat, Animal);
};
static void Meow(Animal *THIS){
    printf("Meow!My name is %s!\n", THIS->name);
}

static int Cat_loadSt(StAnimal *THIS, void *PARAM){
    THIS->talk = (void *)Meow;
    return 0;
}
ASM_EX(Cat,Animal, NULL, NULL, Cat_loadSt, NULL)


CLASS_EX(Dog,Animal){
    STATIC_EX(Dog, Animal);
};

static void Woof(Animal *THIS){
    printf("Woof!My name is %s!\n", THIS->name);
}

static int Dog_loadSt(StAnimal *THIS, void *PARAM) {
    THIS->talk = (void *)Woof;
    return 0;
}
ASM_EX(Dog, Animal, NULL, NULL, Dog_loadSt, NULL)

int main(){
    Animal *animals[4000];
    StAnimal *f;
    int i = 0;
    for (i=0; i<4000; i++)
    {
        if(i%2==0)
            animals[i] = NEW(Dog,"Jack");
        else
            animals[i] = NEW(Cat,"Lily");
    };
    f = ST(animals[0]);
    for(i=0; i<4000; ++i) {
        f->talk(animals[i]);
    }
    for (i=0; i<4000; ++i) {
        DELETE0(animals[i]);
    }
    return 0;
}

Voici mon site de projet (je n'ai pas assez de temps pour écrire en. doc,cependant le doc en chinois est beaucoup mieux).

OOC-GCC

5
répondu dameng 2015-12-31 19:34:04
la source

il y a un exemple d'héritage utilisant le C dans l'exposé de 1996 de Jim Larson donné lors du Section 312 Programming Lunchtime Seminar here: High and Low-Level C .

4
répondu Judge Maygarden 2008-12-09 07:50:32
la source

quels articles ou livres sont bons à utiliser les concepts OOP en C?

Dave Hanson C Interfaces et Implémentations est excellent sur l'encapsulation et de nommage et de très bonne sur l'utilisation de pointeurs de fonction. Dave n'essaie pas de simuler l'héritage.

4
répondu Norman Ramsey 2010-04-29 06:38:33
la source

de la programmation orientée objet n'est qu'un paradigme qui place les données est plus important que le code dans les programmes. La POO n'est pas une langue. Ainsi, comme c simple est un langage simple, OOP dans C simple est aussi simple.

4
répondu anonyme 2012-01-16 03:50:59
la source

une chose que vous pourriez vouloir faire est de regarder dans la mise en œuvre du Xt boîte à outils pour x Fenêtre . Bien sûr, il est de plus en plus long dans la dent, mais la plupart des structures utilisées ont été conçues pour fonctionner de manière OO dans le C. Généralement, cela signifie ajouter une couche supplémentaire d'indirecte ici et là et de concevoir des structures à poser les uns sur les autres.

vous pouvez vraiment faire des lots de la façon de OO situé dans C de cette façon, même si cela semble parfois le cas, les concepts OO ne sont pas sortis complètement de l'esprit de #include<favorite_OO_Guru.h> . Ils constituaient en réalité un grand nombre des meilleures pratiques établies de l'époque. Les langues et les systèmes oo ne distillaient et amplifiaient que des parties du programme zeitgeist du jour.

4
répondu Peter Mortensen 2015-12-31 19:26:23
la source

la réponse à La question est "Oui, vous le pouvez".

le kit c orienté objet (OOC) est pour ceux qui veulent programmer de manière orientée objet, mais colle sur le bon vieux C aussi. OOC implémente des classes, héritage simple et multiple, gestion des exceptions.

Caractéristiques

"151900920 • * utilise uniquement des macros et des fonctions C, Aucune extension de langue n'est nécessaire! (ANSI-C)

* code source facile à lire pour votre application. On a pris soin de rendre les choses aussi simples que possible.

* héritage unique de classes

• héritage Multiple par interfaces et mixins (depuis la version 1.3)

* mise en œuvre des exceptions (en C!pur!)

"151900920 • * fonctions virtuelles pour classes

• outil externe pour la mise en œuvre facile de classe

Pour plus de détails, visitez http://ooc-coding.sourceforge.net / .

4
répondu Sachin Mhetre 2015-12-31 19:36:39
la source

on dirait que les gens essayent d'imiter le style C++ en utilisant C. Mon point de vue est que faire de la programmation orientée objet C est vraiment faire de la programmation orientée struct. Cependant, vous pouvez obtenir des choses comme la reliure tardive, encapsulation, et l'héritage. Pour l'héritage, vous définissez explicitement un pointeur vers les structures de base dans votre sous-structure et c'est évidemment une forme d'héritage multiple. Vous aurez également besoin de déterminer si votre

//private_class.h
struct private_class;
extern struct private_class * new_private_class();
extern int ret_a_value(struct private_class *, int a, int b);
extern void delete_private_class(struct private_class *);
void (*late_bind_function)(struct private_class *p);

//private_class.c
struct inherited_class_1;
struct inherited_class_2;

struct private_class {
  int a;
  int b;
  struct inherited_class_1 *p1;
  struct inherited_class_2 *p2;
};

struct inherited_class_1 * new_inherited_class_1();
struct inherited_class_2 * new_inherited_class_2();

struct private_class * new_private_class() {
  struct private_class *p;
  p = (struct private_class*) malloc(sizeof(struct private_class));
  p->a = 0;
  p->b = 0;
  p->p1 = new_inherited_class_1();
  p->p2 = new_inherited_class_2();
  return p;
}

    int ret_a_value(struct private_class *p, int a, int b) {
      return p->a + p->b + a + b;
    }

    void delete_private_class(struct private_class *p) {
      //release any resources
      //call delete methods for inherited classes
      free(p);
    }
    //main.c
    struct private_class *p;
    p = new_private_class();
    late_bind_function = &implementation_function;
    delete_private_class(p);

compiler avec c_compiler main.c inherited_class_1.obj inherited_class_2.obj private_class.obj .

donc le Conseil est de s'en tenir à un style C pur et de ne pas essayer de forcer dans un style C++. De plus, cette façon se prête à une façon très propre de construire une API.

4
répondu 4 revs, 2 users 91%user2074102 2015-12-31 19:38:53
la source

voir http://slkpg.byethost7.com/instance.html pour encore une autre torsion sur OOP en C. Il met l'accent sur les données d'instance pour la rentrée en utilisant juste natif C. l'héritage Multiple est fait manuellement en utilisant des enveloppements de fonction. La sécurité de Type est maintenue. Voici un petit échantillon:

typedef struct _peeker
{
    log_t     *log;
    symbols_t *sym;
    scanner_t  scan;            // inherited instance
    peek_t     pk;
    int        trace;

    void    (*push) ( SELF *d, symbol_t *symbol );
    short   (*peek) ( SELF *d, int level );
    short   (*get)  ( SELF *d );
    int     (*get_line_number) ( SELF *d );

} peeker_t, SlkToken;

#define push(self,a)            (*self).push(self, a)
#define peek(self,a)            (*self).peek(self, a)
#define get(self)               (*self).get(self)
#define get_line_number(self)   (*self).get_line_number(self)

INSTANCE_METHOD
int
(get_line_number) ( peeker_t *d )
{
    return  d->scan.line_number;
}

PUBLIC
void
InitializePeeker ( peeker_t  *peeker,
                   int        trace,
                   symbols_t *symbols,
                   log_t     *log,
                   list_t    *list )
{
    InitializeScanner ( &peeker->scan, trace, symbols, log, list );
    peeker->log = log;
    peeker->sym = symbols;
    peeker->pk.current = peeker->pk.buffer;
    peeker->pk.count = 0;
    peeker->trace = trace;

    peeker->get_line_number = get_line_number;
    peeker->push = push;
    peeker->get = get;
    peeker->peek = peek;
}
2
répondu slkpg 2012-07-28 17:08:11
la source

je suis un peu en retard à la fête, mais je veux partager mon expérience sur le sujet: je travaille avec des trucs embarqués ces jours - ci, et le seul compilateur (fiable) que j'ai est C, de sorte que je veux appliquer l'approche orientée objet dans mes projets embarqués écrit en C.

la plupart des solutions que j'ai vu jusqu'à présent utilisent des typographies fortement, donc nous perdons la sécurité de type: compilateur ne vous aidera pas si vous faites une erreur. Ceci est totalement inacceptable.

exigences que j'ai:

  • éviter autant que possible les typographies, pour ne pas perdre la sécurité du type;
  • polymorphisme: nous devrions pouvoir utiliser des méthodes virtuelles, et l'utilisateur de la classe ne devrait pas savoir si une méthode particulière est virtuelle ou non;
  • héritage Multiple: Je ne l'utilise pas souvent, mais parfois je veux vraiment qu'une classe implémente plusieurs interfaces (ou étendre plusieurs superclasses).

j'ai expliqué mon approche en détail dans cet article: Programmation Orientée Objet en C ; en plus, il y a un utilitaire pour l'autogénération du code de boilerplate pour les classes de base et dérivées.

2
répondu Dmitry Frank 2015-03-19 16:57:45
la source

j'ai construit une petite bibliothèque où j'ai essayé et pour moi ça fonctionne bien réel. J'ai donc pensé que je partage l'expérience.

https://github.com/thomasfuhringer/oxygen

single inheritance peut être implémenté assez facilement en utilisant une structure et en l'étendant pour chaque autre classe d'enfant. Un casting simple à la structure de parent permet d'utiliser des méthodes de parent sur tous les descendants. Aussi longtemps que vous savez qu'une variable les points à une structure tenant ce genre d'objet que vous pouvez toujours lancer à la classe root et faire l'introspection.

comme il a été mentionné, les méthodes virtuelles sont un peu plus compliquées. Mais ils sont réalisables. Pour garder les choses simples j'utilise juste un tableau de fonctions dans la structure de description de classe que chaque classe d'enfant copie et repopule des fentes individuelles où requis.

héritage Multiple serait plutôt compliqué à mettre en œuvre et vient avec un impact significatif sur le rendement. Donc je le laisse. Je considère qu'il est souhaitable et utile dans un certain nombre de cas de modéliser proprement les circonstances de la vie réelle, mais dans probablement 90% des cas un seul héritage couvre les besoins. Et l'héritage unique est simple et ne coûte rien.

aussi je ne me soucie pas de la sécurité de type. Je pense que vous ne devriez pas dépendre du compilateur pour vous empêcher de programmer des erreurs. Et cela ne vous protège que d'une petite partie des erreurs de toute façon.

typiquement, dans un environnement orienté objet, vous voulez aussi implémenter le comptage de référence pour automatiser la gestion de la mémoire dans la mesure du possible. Donc j'ai aussi mis un compte de référence dans la classe racine "objet" et une fonctionnalité pour encapsuler l'allocation et la désallocation de la mémoire tas.

tout est très simple et maigre et me donne l'essentiel de OO sans me forcer à traiter avec le monstre qui est C++. Et je garde la flexibilité de rester en C la terre, ce qui, entre autres choses, facilite l'intégration des bibliothèques tierces.

2
répondu Thomas F. 2016-03-02 11:38:39
la source

je propose D'utiliser L'Objectif-C, qui est un sur-Ensemble de C.

alors que L'Objectif-C A 30 ans, il permet d'écrire du code élégant.

http://en.wikipedia.org/wiki/Objective-C

1
répondu SteAp 2010-08-28 02:42:13
la source

Oui, mais je n'ai jamais vu personne tenter de mettre en œuvre une sorte de polymorphisme avec C.

0
répondu Paul Morel 2008-12-09 07:06:03
la source

Autres questions sur c object oop