How can Inheritance be modelled using C? [duplicate]
It is very simple to go like this:
struct parent {
int foo;
char *bar;
};
struct child {
struct parent base;
int bar;
};
struct child derived;
derived.bar = 1;
derived.base.foo = 2;
But if you use MS extension (in GCC use -fms-extensions
flag) you can use anonymous nested struct
s and it will look much better:
struct child {
struct parent; // anonymous nested struct
int bar;
};
struct child derived;
derived.bar = 1;
derived.foo = 2; // now it is flat
You can definitely write C in a (somewhat) object-oriented style.
Encapsulation can be done by keeping the definitions of your structures in the .c file rather than in the associated header. Then the outer world handles your objects by keeping pointers to them, and you provide functions accepting such pointers as the "methods" of your objects.
Polymorphism-like behavior can be obtained by using functions pointers,
usually grouped within "operations structures",
kind of like the "virtual method table" in your C++ objects
(or whatever it's called).
The ops structure can also include other things such as
constants whose value is specific to a given "subclass".
The "parent" structure can keep a reference to ops-specific data
through a generic void*
pointer.
Of course the "subclass" could repeat the pattern for multiple levels
of inheritance.
So, in the example below, struct printer
is akin to an abstract class,
which can be "derived" by filling out a pr_ops
structure,
and providing a constructor function wrapping pr_create()
.
Each subtype will have its own structure which will be "anchored"
to the struct printer
object through the data
generic pointer.
This is demontrated by the fileprinter
subtype.
One could imagine a GUI or socket-based printer,
that would be manipulated regardless by the rest of the code
as a struct printer *
reference.
printer.h:
struct pr_ops {
void (*printline)(void *data, const char *line);
void (*cleanup)(void *data);
};
struct printer *pr_create(const char *name, const struct output_ops *ops, void *data);
void pr_printline(struct printer *pr, const char *line);
void pr_delete(struct printer *pr);
printer.c:
#include "printer.h"
...
struct printer {
char *name;
struct pr_ops *ops;
void *data;
}
/* constructor */
struct printer *pr_create(const char *name, const struct output_ops *ops, void *data)
{
struct printer *p = malloc(sizeof *p);
p->name = strdup(name);
p->ops = ops;
p->data = data;
}
void pr_printline(struct printer *p, const char *line)
{
char *l = malloc(strlen(line) + strlen(p->name) + 3;
sprintf(l, "%s: %s", p->name, line);
p->ops->printline(p->data, l);
}
void pr_delete(struct printer *p)
{
p->ops->cleanup(p->data);
free(p);
}
Finally, fileprinter.c:
struct fileprinter {
FILE *f;
int doflush;
};
static void filepr_printline(void *data, const char *line)
{
struct fileprinter *fp = data;
fprintf(fp->f, "%s\n", line);
if(fp->doflush) fflush(fp->f);
}
struct printer *filepr_create(const char *name, FILE *f, int doflush)
{
static const struct ops = {
filepr_printline,
free,
};
struct *fp = malloc(sizeof *fp);
fp->f = f;
fp->doflush = doflush;
return pr_create(name, &ops, fp);
}
Yes, you can emulate heritance en C using the "type punning" technique. That is the declaration of the base class (struct) inside the derived class, and cast the derived as a base:
struct base_class {
int x;
};
struct derived_class {
struct base_class base;
int y;
}
struct derived_class2 {
struct base_class base;
int z;
}
void test() {
struct derived_class d;
struct derived_class2 d2;
d.base.x = 10;
d.y = 20;
printf("x=%i, y=%i\n", d.base.x, d.y);
}
But you must to declare the base class in the first position in you derived structure, if you want to cast the derived as base in a program:
struct base *b1, *b2;
b1 = (struct base *)d;
b2 = (struct base *)d2;
b1->x=10;
b2->x=20;
printf("b1 x=%i, b2 x=%i\n", b1->x, b2->x);
In this snippet you can use the base class only.
I use this technique in my projects: oop4c