Inheritance in C?

Hello,

I am trying to do simple inheritance in C. Please view the short sample code below ...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
===========================================
#include <stdio.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; 

//////////////////////////////////////////////////////////////Person methods

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

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

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

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

//////////////////////////////////////////////////////////////Worker methods

void setJob(Worker *ptr, char j[25]) 
{ 
strcpy(ptr->job, j); 
//...
ptr->person.age = 10; // Setting to inherited data !
} 

char* getJob(Worker *ptr) 
{ 
int y;
y = ptr->person.age; // Getting inherited data !
//...
return ptr->job; 
} 

void setSalary(Worker *ptr, int s) 
{ 
ptr->salary = s; 
//...
ptr->person.age = 11; // Setting to inherited data !
} 

int getSalary(Worker *ptr) 
{ 
int y;
y = ptr->person.age; // Getting inherited data !
y = y +10; // Adding logic to inherited data!
//...
return ptr->salary; 
} 

//////////////////////////////////////////////////////////////Boss methods

void setEmployees(Boss *ptr, int e) 
{ 
ptr->employees = e; 
ptr->worker.salary = 50; // Setting to inherited data !
ptr->worker.person.age = 25; // Setting to inherited data !
} 

int getEmployees(Boss *ptr) 
{ 
int y, z;
y = ptr->worker.salary; // Getting inherited data from worker !
z = ptr->worker.person.age; // Getting inherited data from person!
z = z + y; // Adding logic to inherited data!
//...
return ptr->employees; 
} 


int main() 
{ 
Person person;
Worker worker;
Boss boss;

// Person methods
setName(&person, "Nick"); 
setAge(&person, 19); 

// Worker methods
setJob(&worker, "CEO"); 
setSalary(&worker, 500000); 

// Boss methods 
setEmployees(&boss, 50); 

return 0; 
} 
==================================


QUESTIONS:

1) Above, we have a program that depending on the object I create, there seems to be a particular inheritance of data from previous structs. So if I create a Person object, well, in the Person's metods, I am pretty much restricted in using the data/methods in the Persons structure. If I create a worker object, well, in the Worker's metods, I am pretty much restricted to using the data/methods in the worker and person structures. And if I create a boss object, in the Bosse's methods, I am not restricted at all since I have access to all data/methods from all the structures. If possible, I would really appreciate some form of reassurance as to weather I am getting this or not. Thanks.

2) A sample comment from the former code stated:

"/* order matters, this must be first */ "

I have played around with this by changing the order and it still works??? Why would the author of the original sample state that the order matters? I tried it with putting "Person person;" after "int salary;" and executed the program without any problems???

3) Would some parts of programs in a given application *only* require inheritance? Is Just doing inheritance solely without integrating any polymorphism (as sample shown above) pointless. I mean I am alitlle confused as when to use inhritance only and when to use inheritance with polynorphisim?

4) Is replacing the void* pointers from the previous example by actual object types as shown in code sample above the correct way to implement inheritance?

Thanks for all feedback! Any anwers to these questions would greatly clear some grey zones about inheritance.

PS, I am in the embedded field and using Micrchip Pics. Pics don't have a C++ compiler so I am limitied to using C. I am trying to integrate a minimum of oop features in my C code. I know it won't be as clean as c++, but that's okay... I am simply experimenting right now.

rob
1) Good job. Read the caveat below.

2) It matters if you pass a pointer to a Worker to the setName() function, because the setName() function expects a pointer to a Person. If you move stuff around in the structure then the functions will fail, because they have no idea that you've done that.

3) Yes, inheritance without polymorphism is pointless.

4) I'm lost here. The code you posted hasn't any void pointers.

Caveats. Polymorphism works because an object knows what kind of thing it is, so you can override a method to perform correctly for the given object type. This is only possible when the object contains a "virtual table" -- a list of pointers to functions appropriate to that given object. And it also means that a function that expects one kind of thing (a pointer to a base class) can properly operate on another kind of thing (a pointer to a derived class).

Here is a simple example:

An "animal" can "speak". This is our base class. (It should be abstract, but that's beyond the scope of this discussion.)
A "dog" speaks by saying "woof".
A "cat" speaks by saying "meow".

A function that gets a list of animals (some dogs, some cats) can tell each animal to "speak". The function doesn't have any clue what kind of animal each one is, only that it is a type of (or derivation of) an animal. But each animal correctly speaks.

Here it is in C++:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
using namespace std;

struct animal
  {
  virtual void speak() const = 0;
  };

struct dog: public animal
  {
  virtual void speak() const { cout << "woof!\n"; }
  };

struct cat: public animal
  {
  virtual void speak() const { cout << "meow.\n"; }
  };

int main()
  {
  // our list of pointers to animals
  animal* animals[ 4 ] = {
    new dog,
    new cat,
    new dog,
    new dog
    };

  // apply some polymorphic behavior here
  // (each animal says the correct thing)
  for (int n = 0; n < 4; n++)
    animals[ n ]->speak();

  // don't forget to clean up
  for (int n = 0; n < 4; n++)
    delete animals[ n ];

  return 0;
  }
woof!
meow.
woof!
woof!

A lot of things are going on behind the scenes there. In C, there isn't any "behind the scenes" so we need to do it ourselves. Lets give it a go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <stdio.h>
#include <stdlib.h>

/* A simple cheat for readability. Caveats apply. */
typedef enum { false, true } bool;

/* ------------------------------------------------------- **
 * First, we need our "virtual table" of
 * functions used by descendant objects.
 * Notice how the function takes a void*
 * as argument. This is important!
 */
struct animal_vtable
  {
  void (*p_speak)( void* );
  /* any other virtual base class functions go here */
  };

/* ...and descendant classes need their virtual tables too */
struct dog_vtable
  {
  struct animal_vtable animal_vtable;
  /* virtual functions introduced in the dog class go here */
  };

struct cat_vtable
  {
  struct animal_vtable animal_vtable;
  /* virtual functions introduced in the cat class go here */
  };

/* ------------------------------------------------------- **
 * Here we introduce the classes.
 * In C++, the virtual table is a "hidden" member of a
 * class, but in C it cannot be hidden. Make sure to list
 * it FIRST!
 */
struct animal
  {
  struct animal_vtable* vtable;
  int age;
  };

struct dog
  {
  struct animal animal;
  bool drools;
  }
  dog;

struct cat
  {
  struct animal animal;
  bool sheds;
  }
  cat;

/* ------------------------------------------------------- **
 * Now we can declare the polymorphic "speak" function
 */
void animal_speak( void* animal )
  {
  ((struct animal*)animal)->vtable->p_speak( animal );
  }

/* And while we are at it, the individual speak functions */
void dog_speak( void* dog )
  {
  puts( "woof!" );
  }

void cat_speak( void* cat )
  {
  puts( "meow." );
  }

/* ------------------------------------------------------- **
 * Here are our vtables.
 */
struct animal_vtable animal_vtable = {   NULL         };
struct dog_vtable    dog_vtable    = { { &dog_speak } };
struct cat_vtable    cat_vtable    = { { &cat_speak } };

/* ------------------------------------------------------- **
 * And we need the required constructors for each class type.
 * Notice how the constructor does NOT call calloc() -- that
 * is the job of the allocator.
 * Also notice how the constructor for derived types call
 * the constructor for the parent type!
 */
struct animal* new_animal( void* space )
  {
  struct animal* animal = space;

  animal->vtable = &animal_vtable;
  animal->age = 0;

  return animal;
  }

struct animal* new_dog( void* space )
  {
  struct dog* dog = (struct dog*) new_animal( space );

  dog->animal.vtable = (struct animal_vtable*) &dog_vtable;
  dog->drools        = true;

  return (struct animal*) dog;
  }

struct animal* new_cat( void* space )
  {
  struct cat* cat = (struct cat*) new_animal( space );

  cat->animal.vtable = (struct animal_vtable*) &cat_vtable;
  cat->sheds         = false;  /* not until older... */

  return (struct animal*) cat;
  }

/* ------------------------------------------------------- **
 * This is our replacement for C++'s "new" function, which
 * does two things: it allocates and zeros space for the
 * new object, and then applies the objects constructor to
 * that space.
 * In C, of course, our allocator is calloc() and the
 * constructor must be spliced together by the preprocessor.
 */
#define new( type ) new_ ## type ( calloc( 1, sizeof( struct type ) ) )

/* Similar considerations must be made for destructors, if
 * you use them, except that destructors must be a virtual
 * function!
 */

/* --------------------------- */
int main()
  {
  int n;
  struct animal* animals[ 4 ];
  animals[ 0 ] = new( dog );
  animals[ 1 ] = new( cat );
  animals[ 2 ] = new( dog );
  animals[ 3 ] = new( dog );

  for (n = 0; n < 4; n++)
    animal_speak( animals[ n ] );

  for (n = 0; n < 4; n++)
    free( animals[ n ] );

  return 0;
  }
woof!
meow.
woof!
woof!

This is a very simplified version for you, but I think that it covers all the basics, at least enough for you to move forward.

Remember that there is a lot of safety built in to using C++ classes. In C, all bets are off because you are doing it yourself!

Even that simple thing at the top typedef enum { false, true } bool; is dangerous, because it only make things look like they do in C++, but it is not an actual type. Hence, you cannot say something like if (my_bool == true) because it will fail (sizeof( my_bool ) * 8 - 1) times out of (sizeof( my_bool ) * 8). If you don't understand why, then you should probably not be messing with it.

Everything else here is more of the same danger.

BTW, this is not the only way to do this kind of thing. There are other methods, some much more advanced. Remember, this is a simple example to get you started. The more you mess around with it, the better an understanding you will get of it, and the better you will see the limitations of doing OOP in C.

Hope this helps.
Hello Duoas,

First and foremost I would like to sincerely thank you for your effort in answering my post. Its very nice of you.

Darn it, I had a full reply and lost it when I changed tabs....! So I won't re-write all that again... cause I don't even remeber it. So I will be breif.

>1) Good job. Read the caveat below.
Thanks

>2) It matters if you pass a pointer to a Worker to the setName() function, because the setName
>() function expects a pointer to a Person. If you move stuff around in the structure then the >functions will fail, because they have no idea that you've done that.

So you are saying that if I would pass a Worker to setName and then I would want to access some worker data members... it would fail????


>3) Yes, inheritance without polymorphism is pointless.
Interesting! You see, I will be facing a dilema in the sense that, I don't if I should just use inheritance for another issue I have... let me explain. I have a large structure with many members. Some of the members really belong together in terms of type of data... for example, a group of these members may store the informetion for a message... something like this:

char user_msg_font[17];
unsigned short x_user_msg_pos[3];
unsigned short y_user_msg_pos[3];
unsigned char enable_qck_msg;
char quick_msg[17];
and so I the above group of members make up the information required to display a message. But the thing is that I may have another group members similar to the above members which are responsible to store the information for say... a backgroud... and so forth. I may have 10 or 15 of these groups like this... so you can imagine how long my structure is. I would like to resolve this problem by sreplacing such group of members by a pointer to a structure holding the actual information for a message. What could be my options?

>4) I'm lost here. The code you posted hasn't any void pointers.
Oh! forget about this... I made a mistake... the original sample I found on the net had void* as the first parameter of every function. I removed them.

>Caveats. Polymorphism works because an object knows what kind of thing it is, so you can >override a method to perform correctly for the given object type. This is only possible when the >object contains a "virtual table" -- a list of pointers to functions appropriate to that given object. >And it also means that a function that expects one kind of thing (a pointer to a base class) can >properly operate on another kind of thing (a pointer to a derived class).

I really like your C sample code. Yes I agree I really like C++ its so much cleaner. On another note what is your take on the following resolution of C with OOP at:

http://www.codeproject.com/KB/cpp/InheritancePolymorphismC.aspx

The reason why I bring it up is that I was wondering if my link contains any problems. If so, I would rather use your sample and build on that.

Mind you I am an embedded programmer, I know C/C++ but very very far from being an expert. hehe!

I would really like your opinion on that OOP in C link !!!!!!

later!

Sincere regards
Rob


Topic archived. No new replies allowed.