543 votes

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

Pouvez-vous écrire du code orienté objet en C? Surtout en ce qui concerne le polymorphisme.


Voir aussi: http://stackoverflow.com/questions/415452/object-orientation-in-c

389voto

mepcotterell Points 2026

Oui. En fait, Axel Schreiner fournit gratuitement son livre "Programmation Orientée Objet en ANSI-C" qui couvre le sujet de manière assez approfondie.

387voto

paxdiablo Points 341644

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 tenir à la fois les données et une liste de pointeurs de fonction à point pour les fonctions pertinentes pour ce type de données.

Ainsi, dans une communication de la classe, vous pourriez ouvrir, lire, écrire et près d'appel qui serait maintenue que quatre pointeurs de fonction dans la structure, à côté des données d'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, ces segments de code ci-dessus pourrait être en fait un "constructeur" comme rs232Init().

Lorsque vous 'inherit' à partir de cette classe, il suffit de changer les pointeurs au point de vos propres fonctions. Tout le monde qui a appelé à ces fonctions, serait-il le faire à travers les pointeurs de fonction, vous donnant votre polymorphisme:

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

Comme une sorte de manuel de la vtable.

Vous pourriez même avoir des classes virtuelles, en définissant les pointeurs à NULL le comportement serait légèrement différent du C++ (un core dump au moment de l'exécution plutôt que d'une erreur au moment de la compilation).

Voici un exemple de code qui illustre cela. D'abord le niveau supérieur de la structure de classe:

#include <stdio.h>

// The top-level class.

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

Ensuite, nous avons les fonctions pour le TCP 'sous-classe':

// 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 la HTTP ainsi:

// 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 de test 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;
}

Ce produit de la sortie:

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

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

99voto

nategoose Points 8011

Les espaces de noms sont souvent fait par:

stack_push(thing *)

au lieu de

stack::push(thing *)

Pour faire un c struct dans quelque chose comme une classe c++, vous pouvez vous 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 faire:

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 statique, sauf que certains de prendre un ce *

66voto

Miro Points 925

Je crois qu'en plus d'être utile dans son propre droit, la mise en œuvre de la programmation orientée objet en C est un excellent moyen pour apprendre la programmation orientée objet et de comprendre son fonctionnement interne. L'expérience de beaucoup de programmeurs a montré que l'utilisation d'une technique de manière efficace et en toute confiance, un programmeur doit comprendre comment les concepts sous-jacents sont finalement mis en œuvre. L'émulation de classes, l'héritage et le polymorphisme en C enseigne tout cela.

Pour répondre à la question d'origine, voici quelques ressources qui nous enseignent comment faire de la POO en C:

EmbeddedGurus.com blog post "Objet de base de la programmation en C", montre comment mettre en œuvre les classes et l'héritage simple 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 de macros du préprocesseur: http://www.state-machine.com/resources/cplus_3.0_manual.pdfl'exemple de code est disponible à partir de http://www.state-machine.com/resources/cplus_3.0.zip

32voto

tvanfosson Points 268301

Je l'ai vu faire. Je ne le recommande pas. C++ à l'origine commencé comme un préprocesseur qui produit du code C comme une étape intermédiaire.

Essentiellement, ce que vous faire est de créer un tableau de répartition pour l'ensemble de vos méthodes dans lesquelles vous pouvez stocker vos références de fonction. La dérivation d'une classe serait une copie d'envoi de ce tableau et de remplacer les entrées que vous vouliez remplacer, avec vos nouvelles "méthodes" d'avoir à appeler la méthode originale si elle veut invoquer la méthode de base. Finalement, vous réécrire en C++.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X