20211018080620 - Objects in C
We can use C for Object-Oriented Programming (OOP). Although C does not support OOP natively, we can implement the missing features ourselves to support it.
# Classes
We count with the keyword `struct` in the C language to create custom data types (a.k.a. structures) containing other datatypes as members.
In OOP, classes are also custom data types with two types of members:
- attributes (or data members, or member variables), and
- methods (or member functions).
When using C's structures, we can implement attributes as variables and methods as function pointers.
## The `Dog` class
Consider that we want to create a `Dog` class that has the following members:
- `name`: a string containing the name of the dog.
- `play()`: a function that prints on the screen that the dog is playing.
- `bark()`: a function that prints on the screen that the dog is barking.
```
.----------------.
| Dog |
|----------------|
| + name:char* |
|----------------|
| + play() |
| + bark() |
'----------------'
```
We can declare such a class as shown in Listing 1.
```c
struct Dog {
char * name;
void (*play)(struct Dog * self);
void (*bark)(struct Dog * self);
};
```
**Listing 1**.
Notice that the member functions have a reference to a Dog structure pointer "self". Member functions need this pointer to know over which instance they are acting on. A function without such a pointer, would not be able to access the rest of the members of the class.
## Method implementations
Now, let's implement the two methods: play and bark.
```c
void play(struct Dog * self) {
printf(
"%s, the dog, is playing.\n",
self->name);
}
void bark(struct Dog * self) {
printf(
"%s ways 'woof!'.\n",
self->name);
}
```
**Listing 2**.
## `Dog` objects
To use our `Dog` class in an application, we will need to create objects out of it. And to do so, we must provide the following set of functions:
- a **creator**: provides a pointer to a memory block big enough to store the object information.
- an **initializer**: initializes the memory given by the creator function with the initial attribute values and sets the method pointers to the implementations.
- a **destroyer**: frees the memory block that our object is using.
```c
struct Dog * createDog(){
struct Dog * newDog;
newDog = (struct Dog *)malloc(
sizeof(struct Dog));
memset(
newDog,
0,
sizeof(struct Dog));
return newDog;
}
void initDog(
struct Dog * dog,
char * name
) {
unsigned int nameLength;
nameLength = strlen(name);
dog->name = (char*)malloc(
nameLength + 1);
strncpy(
dog->name,
name,
nameLength);
dog->play=play;
dog->bark=bark;
}
void destroyDog(
struct Dog** dog
) {
if ((*dog)->name) {
free((*dog)->name);
}
free(*dog);
*dog = NULL;
}
```
**Listing 3**.
**Other creation options**. Object creation functions may use more allocation options than just calling malloc. In embedded systems, often static allocation will be a better option. In such a case, we could declare an array of ̀Dog` structures and mark them as busy or free as we call `createDog()` and `destroyDog()` respectively.
## Example application
Now we can use our Dog class to create objects.
```c
int main() {
struct Dog * astro = createDog();
initDog(astro, "Astro");
astro->play(astro);
astro->bark(astro);
destroyDog(&astro);
return 0;
}
```
**Listing 4**.
## Full code
```c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
struct Dog {
char * name;
void (*play)(struct Dog * self);
void (*bark)(struct Dog * self);
};
void play(struct Dog * self) {
printf(
"%s, the dog, is playing.\n",
self->name);
}
void bark(struct Dog * self) {
printf(
"%s ways 'woof!'.\n",
self->name);
}
struct Dog * createDog(){
struct Dog * newDog;
newDog = (struct Dog *)malloc(
sizeof(struct Dog));
memset(
newDog,
0,
sizeof(struct Dog));
return newDog;
}
void initDog(
struct Dog * dog,
char * name
) {
unsigned int nameLength;
nameLength = strlen(name);
dog->name = (char*)malloc(
nameLength + 1);
strncpy(
dog->name,
name,
nameLength);
dog->play=play;
dog->bark=bark;
}
void destroyDog(
struct Dog** dog
) {
if ((*dog)->name) {
free((*dog)->name);
}
free(*dog);
*dog = NULL;
}
int main() {
struct Dog * astro = createDog();
initDog(astro, "Astro");
astro->play(astro);
astro->bark(astro);
destroyDog(&astro);
return 0;
}
```
**Listing 5**.
## Standard output
```
Astro, the dog, is playing.
Astro ways 'woof!'.
```
**Listing 6**.
Comentarios
Publicar un comentario