Post

Object Oriented Programming in C part IV: Inheritance

In OOP, inheritance is a foundational concept that enables classes to inherit attributes and methods from others. Let's explore how this is achieved in C.

1. Basic about inheritance

Inheritance, a fundamental concept enabling classes to inherit attributes and methods from others, fosters hierarchical relationships in object-oriented programming. While C lacks the class concept, it allows for implementing inheritance through clever techniques, laying the groundwork for the powerful concept of polymorphism.

2. Apply inheritance in C

In the previous blog OOP: Object Blog we talked about how to create an object with OPP style. Let鈥檚 explore how we can create a base object type and implement inheritance in a derived type using C. Look at to this example, we have a base object type person that contains base attributes like name, age, along with base methods like talk() and work():

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct {
    char name[MAX_LENGTH];
    char language[MAX_LENGTH];
    int age;
} person_t;

int person_init(person_t *self, const char *name);
int person_deinit(person_t *self);

/* Pubic methods. */
int person_talk(person_t *self, const char *speech);
int person_work(person_t *self);

Now assume that we want to define an employee object type, also an employee is a person 馃槢, and we don鈥檛 want to re-code all person鈥檚 methods, attributes. So here we consist the person_t as a property of employee_t:

1
2
3
4
5
6
7
8
9
typedef struct
{
    char company[];
    int salary;
    person_t person;
} employee_t;

int employee_init(employee_t *self, const char *name);
int employee_deinit(employee_t *self);

To create and destroy base object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int employee_init(employee_t *self, const char *name)
{
    memset(self, 0, sizeof(employee_t));
    person_init(&self->person, name);

    /* Init another employee's components ... */
    return 0;
}

int employee_deinit(employee_t *self)
{
    person_deinit(&self->person);
    return 0;
}

For using base type attributes and methods in application code, we have two choices:

  1. Access the person attributes, methods directly (Public Inheritance).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
     int main()
     {
         employee_t John = {0};
         employee_init(&John, "John");
    
         printf("Employee name: %s\n", John.person.name);
         printf("Employee age: %s\n", John.person.age);
    
         person_talk(&John.person, "Hi, I'm John!");
    
         employee_init(&John);
     }
    
  2. Wrapping them by employee methods (Private Inheritance).

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
     const char *employee_get_name(employee_t *self)
     {
         return (const char *)self->person.name;
     }
    
     int employee_talk(employee_t *self, const char *speech)
     {
         return person_talk(&self->person, speech);
     }
    

    In the application code:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
     int main()
     {
         employee_t John = {0};
         employee_init(&John, "John");
    
         printf("Employee name: %s\n", employee_get_name(&John));
         employee_talk(&John, "Hi, I'm John!");
    
         employee_init(&John);
     }
    

3. Summary

Inheritance simplifies code reuse, establishes relationships between objects, and, most importantly, forms the foundation of polymorphism, enhancing code flexibility and generality for user applications.

This post is licensed under CC BY 4.0 by the author.

Cong Nguyen. Some rights reserved.

Pursue excellence, and success will follow 馃崁