Yes. Early C++ compilers translated the code to C before compiling with a normal C compiler. Today, that's how Vala works. Vala is based on the GLib Object System, which provides object-oriented features for C, but is apparently quite cumbersome. Vala streamlines the process.
As for an example, the following would be loosely equivalent:
Java:
Person.java:
public class Person {
private String name;
private int age;
public Person(String name, int age) { /* code */ }
int getAge () {
return age;
}
String getName () {
return name;
}
void setAge (int age) {
this.age = age;
}
void setName (String name) {
this.name = name;
}
}
C:
person.h:
/*
This line serves two purposes:
a. Declare the struct without revealing its attributes; this is
called an "opaque struct."
b. Let us use just `Person` instead of `struct Person` to refer to
the struct.
*/
typedef struct Person Person;
/* Constructor */
Person* person_new (const char *name, int age);
/* Destructor -- just a wrapper around free() for simple cases */
void person_free (Person *obj);
/* Other methods */
int person_get_age (const Person *obj);
const char* person_get_name (const Person *obj);
void person_set_age (Person *obj, int age);
void person_set_name (Person *obj, const char *name);
person.c:
#include <stdlib.h>
#include "person.h"
/* Now we define the struct. */
struct Person
{
int age;
char *name;
};
Person* person_new (const char *name, int age)
{
Person *obj = (Person*) malloc (sizeof(Person));
obj->name = name;
obj->age = age;
return obj;
}
void person_free (Person *obj)
{
free (obj);
}
int person_get_age (const Person *obj)
{
return obj->age;
}
const char* person_get_name (const Person *obj)
{
return obj->name;
}
void person_set_age (Person *obj, int age)
{
obj->age = age;
}
void person_set_name (Person *obj, const char *name)
{
obj->name = name;
}
Then to use this you'd do something like the following:
Person *me = person_new ("James Jensen", 27);
printf ("Hi, I'm %s and I'm %i years old.\n",
person_get_name (me),
person_get_age (me));
person_free (me);
You can also do inheritance, but it's quite a bit trickier and I'm not really confident in my abilities to do it. Neverthless, the basic approach is something like the following (corrections welcome):
importantperson.c:
#include <stdlib.h>
#include "person.h"
/* Please just imagine I wrote this. */
#include "importantperson.h"
struct ImportantPerson
{
Person super; /* Note that this is NOT a pointer. */
int importance;
};
ImportantPerson* importantperson_new (const char *name, int age,
int importance)
{
ImportantPerson *obj = (ImportantPerson*) malloc (sizeof(ImportantPerson));
person_set_name(&(obj->super), name);
person_set_age(&(obj->super), age);
obj->importance = importance;
return obj;
}
void importantperson_free (ImportantPerson *obj)
{
free (obj);
}
int importantperson_get_age (ImportantPerson *obj)
{
return person_get_age (&(obj->super));
}
/* ...other getters... */
void importantperson_set_age (ImportantPerson *obj, int age)
{
person_set_age (&(obj->super), age);
}
/* ...other setters... */
If I'm not mistaken, due to the way C organizes data, having a non-pointer Person
object as the first attribute of ImportantPerson
also allows you to do this:
ImportantPerson *me = importantperson_new ("James Jensen", 27, 9001);
/* Now I humble myself a bit... */
Person *p = (Person*) me;
printf ("Hi, my name is %s and I'm %i years old.\n",
person_get_name (p),
person_get_age (p));
/*
Now, destructors can be a bit of a problem. If they weren't both just
wrapping free(), this would do the wrong thing:
person_free (p);
*/
importantperson_free (me);
I hope that clears things up. As always, I welcome corrections from more experienced programmers.