C++ School Assignment? Project Due Tomorrow? Chat LIVE With A Programming Expert!

Welcome to Dream.In.Code
Become a C++ Expert!

Join 300,499 C++ Programmers for FREE! Get instant access to thousands of C++ experts, tutorials, code snippets, and more! There are 1,875 people online right now. Registration is fast and FREE... Join Now!




Object Oriented Programming in C

 
Reply to this topicStart new topic

> Object Oriented Programming in C

Rating  5
Darkhack
Group Icon



post 19 Dec, 2008 - 12:03 AM
Post #1


-Introduction-

This tutorial assumes two things. First, that you have a good understanding of the C programming language. Second, that you understand the basic concepts of object oriented programming.

This tutorial is designed to show how one can combine these two areas of knowledge; not to teach each area itself. If you lack knowledge of either C or basic OOP principals, this tutorial is not for you.

There is a myth that one cannot do Object Oriented Programming (OOP) in a language like C. Many developers choose languages like Java or C++ for no other reason than the fact that they support OOP. The fact is however, that one CAN do OOP in C. You may not have as wide a spectrum of features in C, but you can certainly do basic OOP tasks such as creating objects, (multiple) inheritance, etc.

-Classes-

Since C does not have classes, we use structures (structs) instead. The most apparent problem with this is that functions cannot be included into a structure. In reality, this isn't a problem at all. When you create an instance of an object in an OOP language like C++ or Java, the difference between each instance is not the methods, but the data. The methods stay the same and the data changes between instances.

So let's create our first class. We'll use typedefs so that it more closely mimics classes in an OOP language. We'll also create our getter and setter functions.
CODE

#include <stdio.h>
#include <string.h>

typedef struct
{
    char name[12];
    int age;
} Person;

void setName(Person *p, char n[12])
{
    strcpy(p->name, n);
}

char* getName(Person *p)
{
    return p->name;
}

void setAge(Person *p, int a)
{
    p->age = a;
}

int getAge(Person *p)
{
    return p->age;
}

int main()
{
    Person one;
    setName(&one, "Nick");
    setAge(&one, 19);

    printf("%s is %d\n", getName(&one), getAge(&one));

    return 0;
}

The class definition is our struct. Then our methods are just regular functions that take our struct as an argument. If you take a moment to just look at main(), you'll see that it is already looking a lot like an OO program. The calling convention is only slightly different.

C++/Java: one.setName("Nick");
C with OOP: setName(&one, "Nick");

C++/Java: one.getAge();
C with OOP: getAge(&one);

We must pass the address of our struct since we want it to modify our instance and not a copy of it. The calling convention isn't too different though and you get used to it quickly.

-Constructors and Destructors-

Since everyone has a name and an age, it might be ideal to initialize that with a constructor. In C, the constructor must have a different name than the struct or else you will get an error. One idea is to just append _init to the constructor's name and _del (for delete) to our destructor. In this simple example we don't need a destructor but I've included it for completeness so you can see how it would fit in.
CODE

Person Person_init(char name[12], int age)
{
    Person p;
    setName(&p, name);
    setAge(&p, age);
    return p;
}

void Person_del(Person *p)
{
    /* free any dynamically allocated memory */
}

int main()
{
    Person one = Person_init("Nick", 19);
    printf("%s is %d\n", getName(&one), getAge(&one));
    Person_del(&one);
    return 0;
}


-Inheritance-

Person is a very broad term. There are lots of people in the world including children, the disabled, caretakers, unemployed, etc. Therefore, it's not always practical to make the assumption that our Person is also going to be a Worker. However a Worker will always be a Person. So let's create a Worker class that inherits everything from Person.
CODE

typedef struct
{
    Person person;  /* order matters, this must be first */
    char job[25];
    int salary;
} Worker;

If you're a smart cookie (and I'm sure you are), you've already found a problem. All of our functions are expecting a Person. We can't pass a Worker to them! That would just be madness; MADNESS I SAY!

Never fear! Void pointers are here! We can use void pointers so that our functions will accept a parent class or any of its children. Let's see how setAge() would look.
CODE

void setAge(void *ptr, int a)
{
    Person *self = ptr;
    self->age = a;
}

Person p;
Worker w;
setAge(&p, 30);
setAge(&w, 35);

Why does this work? Well, it's important to know that the variable *self, must be the datatype of the parent class. It doesn't matter how many children that parent struct has, but it does need to know which struct ultimately has the variable age. The reason this works is because our child struct has a parent struct inside of it. It doesn't care about the extra data that comes after the Person struct. It only points to that first part. This is why pointers kick so much ass.

As long as we write our function so that *self is the struct with the data we want (and the parent struct is the first data item of the child struct), it will work for all of that struct's children and grandchildren.

Let's look at one more example. In this example we have three structs. The grandparent is Person which has a name and age. The parent is Worker which has a job title and salary. The child is Boss, a type of worker who has an integer specifying the number of employees that work for him. Children can be passed to their parent functions but not vice-versa. Feel free to play around with creating different kinds of objects (Person->Worker->Boss) in main(). Get a feel for how you use objects in main() before you start editing the functions and structs. Here is the code.
CODE

#include <stdio.h>
#include <string.h>

typedef struct
{
    char name[12];
    int age;
} Person;

typedef struct
{
    Person person;    /* order matters, this must be first */
    char job[25];
    int salary;
} Worker;

typedef struct
{
    Worker worker;
    int employees;
} Boss;

void setName(void *ptr, char n[12])
{
    Person *self = ptr;
    strcpy(self->name, n);
}

char* getName(void *ptr)
{
    Person *self = ptr;
    return self->name;
}

void setAge(void *ptr, int a)
{
    Person *self = ptr;
    self->age = a;
}

int getAge(void *ptr)
{
    Person *self = ptr;
    return self->age;
}

void setJob(void *ptr, char j[25])
{
    Worker *self = ptr;
    strcpy(self->job, j);
}

void setSalary(void *ptr, int s)
{
    Worker *self = ptr;
    self->salary = s;
}

char* getJob(void *ptr)
{
    Worker *self = ptr;
    return self->job;
}

int getSalary(void *ptr)
{
    Worker *self = ptr;
    return self->salary;
}

void setEmployees(void *ptr, int e)
{
    Boss *self = ptr;
    self->employees = e;
}

int getEmployees(void *ptr)
{
    Boss *self = ptr;
    return self->employees;
}

int main()
{
    Boss boss;

    /* Person functions */
    setName(&boss, "Nick");
    setAge(&boss, 19);

    /* Worker functions */
    setJob(&boss, "CEO");
    setSalary(&boss, 500000);

    /* Boss functions */
    setEmployees(&boss, 50);

    /* Print information */
    printf("Name: %s\n", getName(&boss));
    printf("Age: %d\n", getAge(&boss));
    printf("Job: %s\n", getJob(&boss));
    printf("Salary: %d\n", getSalary(&boss));
    printf("Employees: %d\n", getEmployees(&boss));

    return 0;
}


-Multiple Inheritance-

With regular inheritance our structs can have as many children and grandchildren as they like. But a child cannot have more than one parent. Having more than one parent is called multiple inheritance. You cannot simply add two structs to a child class either. It doesn't work because the void pointer is only pointing to the first struct.

As far as I know, you can't do true multiple inheritance in C. But then again, neither can you in Java. In the end, this is probably a good thing. A lot of developers would argue that if you need multiple inheritance then you're design is a bad one or is overly complex. Although there does exist a way to fake it in C so it bares some resemblance.
CODE

typedef struct
{

    Mommy mom;
    Daddy dad;
    char name[12];
} Child;

Child c;

momFunction(&c.mom, data);
dadFunction(&c.dad, data);

It's not true multiple inheritance because you can't simply pass the child to the mother or father functions. You have to specify which one your passing.

Again this isn't all bad. By forcing this particular syntax it helps you to avoid "the diamond problem" ( http://en.wikipedia.org/wiki/Diamond_problem ). So our faked multiple inheritance isn't so bad after all.

-Conclusion-

This is just a brief overview of doing basic OOP in C. There are many more complex things which can be done. There are other ways to mimic other OOP features in C and there are also complex systems, such as the GObject system, which provide an OOP interface for C.

If you love C, but felt like there was a gun at your head, forcing you to use C++ or Java in order to gain the benefits of object oriented programming, then perhaps this style of OOP programming in C is the perfect solution.
Go to the top of the page
+Quote Post


Register to Make This Ad Go Away!

vinny358
*



post 3 Apr, 2009 - 02:04 PM
Post #2
Thanks for a clear explanation, it was helpful, keep coming like these..
Go to the top of the page
+Quote Post


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 


Lo-Fi Version Time is now: 11/8/09 04:59AM

Live C++ Help!

Be Social

Dream.In.Code RSS Feed Dream.In.Code LinkedIn Group Follow Us On Twitter Fan Us On Facebook

C++ Tutorials

Reference Sheets

C++ Snippets

DIC Chatroom

Bye Bye Ads

Monthly Drawing

Thumb Drive

Top Contributors

Top 10 Kudos This Month