Simple C inheritance can be simulated very easily. However, this depends on making type coertions (typecasts), which prevents you from using the type system to catch some bugs simple. Let’s take an example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define ANOS 31556952 /* segundos por ano */
#define MKOBJ(obj) do { obj = malloc(sizeof(obj[0])); if (!obj) exit(1); } while (0)
#define RMOBJ(obj) (free(obj), obj = NULL)
/* Estrutura base */
typedef struct pessoa_t {
char * nome;
time_t dt_nascimento;
unsigned char sexo;
} pessoa_t;
/* Estrutura derivada */
typedef struct empregado_t {
pessoa_t base; /* NÃO é um ponteiro */
unsigned int matricula;
time_t dt_admissao;
} empregado_t;
void
imprimir_pessoa(pessoa_t * p) {
printf("%s é %s de %d anos de idade.\n",
p->nome,
p->sexo == 'F' ? "uma moça" : "um rapaz",
(time(NULL) - p->dt_nascimento) / ANOS
);
}
void
imprimir_empregado(empregado_t * e) {
imprimir_pessoa((pessoa_t *) e);
printf("%s entrou na empresa há %d anos.\n",
((pessoa_t *) e)->sexo == 'F' ? "Ela" : "Ele",
(time(NULL) - e->dt_admissao) / ANOS
);
}
static void
criar_pessoa_aux(pessoa_t * self,
const char * nome,
char sexo,
int dia_nasc,
int mes_nasc,
int ano_nasc) {
static struct tm tm;
self->nome = strdup(nome);
self->sexo = sexo;
tm.tm_mday = dia_nasc;
tm.tm_mon = mes_nasc - 1;
tm.tm_year = ano_nasc - 1900;
self->dt_nascimento = mktime(&tm);
}
pessoa_t *
criar_pessoa(const char * nome,
char sexo,
int dia_nasc,
int mes_nasc,
int ano_nasc) {
pessoa_t * result;
MKOBJ(result);
criar_pessoa_aux(result, nome, sexo, dia_nasc, mes_nasc, ano_nasc);
return result;
}
void
destruir_pessoa(pessoa_t * p) {
RMOBJ(p->nome);
RMOBJ(p);
}
empregado_t *
criar_empregado(const char * nome,
char sexo,
int dia_nasc,
int mes_nasc,
int ano_nasc,
int matricula,
int dia_adm,
int mes_adm,
int ano_adm) {
empregado_t * result;
static struct tm tm;
MKOBJ(result);
criar_pessoa_aux((pessoa_t *) result, nome, sexo, dia_nasc, mes_nasc, ano_nasc);
result->matricula = matricula;
tm.tm_mday = dia_adm;
tm.tm_mon = mes_adm - 1;
tm.tm_year = ano_adm - 1900;
result->dt_admissao = mktime(&tm);
return result;
}
void
destruir_empregado(empregado_t * e) {
pessoa_t * p = (pessoa_t *) e;
RMOBJ(p->nome);
RMOBJ(e);
}
int
main(int argc, char ** argv) {
pessoa_t * Alice;
empregado_t * Beto, * Carol;
/* -------nome------- sexo -nascimento-- -matr- ---admissão-- */
Alice = criar_pessoa ("Alice da Silva" , 'F', 6, 12, 1999);
Beto = criar_empregado("Beto da Costa" , 'M', 1, 5, 1974, 11111, 1, 1, 2007);
Carol = criar_empregado("Carol dos Santos", 'F', 9, 3, 1990, 22222, 1, 10, 2015);
imprimir_pessoa(Alice);
imprimir_pessoa((pessoa_t *) Beto);
imprimir_pessoa((pessoa_t *) Carol);
puts("----------\n");
// imprimir_empregado((empregado_t *) Alice); /* SEGFAULT! */
imprimir_empregado(Beto);
imprimir_empregado(Carol);
return 0;
}
As you can see, it takes a lot of work: constructors have to automatically allocate variables and treat allocation errors, and then initialize them; destructors have to delete the strings allocated and then erasing themselves, etc. Of course, this will be the case for any architecture that is not native to the language.
That said, you can see how the empregado_t *
, passed to a function expecting a pessoa_t *
, does the right thing and works perfectly well since the first element of empregado_t
is a structure pessoa_t
. When you do the cast, the employee remains a valid person.
On the other hand, since you are doing type coercion, the compiler does not prevent you from forcing a function that receives a empregado_t *
to receive a pessoa_t *
other than a empregado_t *
. In this case, you will only see the error running (with luck; the coercion can simply access data from another object as if it were the missing fields, and it will silently continue running with inconsistent data).
Like everything else in C, it’s a valid technique, but the programmer has to take responsibility for the Casts what makes.
Related, I don’t know if it’s dup Polymorphism in procedural language
– MarceloBoni
I wrote extensively sonre a related subject before yesterday, here: http://answall.com/questions/180783/como-montar--lista-de-objetos-gen%C3%A9ricos-em-c/181019#181019
– jsbueno
I think it’s possible, but I’m sure it’s not worth it
– zentrunix
I know it’s already answered, but just to comment: I would have to test this with a "pure" C compiler (http://www.cpm.z80.de/small_c.html), and not with a C/C compiler++.
– rmagalhaess