A Practical Guide to Dynamic Polymorphism in C Programming

Dynamic polymorphism is a programming paradigm that enhances code flexibility and maintainability by allowing objects to be treated uniformly while exhibiting different behaviors. This concept is particularly useful in scenarios involving collections of related objects that need to perform specific actions. In this article, we compare two implementations of a toy simulation in C to demonstrate the benefits of dynamic polymorphism
Initial Implementation without Polymorphism
Here we define a Toy struct with a name field and create three instances representing Barbie and Superman toys. Each toy is initialized, and an array of pointers to these toy instances is created. The main function iterates through the array and prints the sound associated with each toy based on its name.
\
#include <stdio.h>
#include <stdlib.h>
// Function prototypes for the sounds made by different toys
char const* barbieSound(void){return "Love and imagination can change the world.";}
char const* supermanSound(void){return "Up, up, and away!";}
// Struct definition for Toy with an character array representing the name of the toy
typedef struct Toy{
char const* name;
}Toy;
int main(void){
//Alocate memory for three Toy instances
Toy* toy1 = (Toy*)malloc(sizeof(Toy));
Toy* toy2= (Toy*)malloc(sizeof(Toy));
Toy* toy3= (Toy*)malloc(sizeof(Toy));
// Initialize the name members of the Toy instances
toy1->name = "Barbie";
toy2->name = "Barbie";
toy3->name = "Superman";
// Create an array of pointers to the Toy instances
Toy* toys[] = {barbie1, barbie2, superman1};
// Output the corresponding sound of each toy given its name
for(int i=0; i < 3; i++){
if (toys[i]->name == "Barbie"){printf("%s\n",toys[i]->name,barbieSound());}
if (toys[i]->name == "SuperMan"){printf("%s\n",toys[i]->name,supermanSound());}
}
// Free the allocated memory for the Toy instances
free(toy1);
free(toy2);
free(toy3);
return 0;
}
While this is functional, it doesn't scale. Whenever we want to add a new toy we need to update the code to handle a new toy type and its sound function which could raise maintenance issues.
\
Enhanced Implementation of Dynamic Polymorphism:
The second code sample uses dynamic polymorphism for a more flexible, scalable application. Here Toy has its ==function pointer== for the sound function. ==Factory functions==(createBarbie(), createSuperMan()) are used to create Barbie and Superman instances, assigning the appropriate sound function to each toy. The makeSound() function demonstrates dynamic polymorphism by calling the appropriate sound function for each toy at run time.
\
#include <stdio.h>
#include <stdlib.h>
// Function prototypes for the sounds made by different toys
char const* barbieSound(void) {return "Love and imagination can change the world.";}
char const* supermanSound(void) {return "I’m here to fight for truth and justice, and the American way.";}
// Struct definition for Toy with a function pointer for the sound function
typedef struct Toy {
char const* (*makeSound)();
} Toy;
// Function to call the sound function of a Toy and print the result
// Demonstrates dynamic polymorphism by calling the appropriate function for each toy
void makeSound(Toy* self) {
printf("%s\n", self->makeSound());
}
// Function to create a Superman toy
// Uses dynamic polymorphism by assigning the appropriate sound function to the function pointer
Toy* createSuperMan() {
Toy* superman = (Toy*)malloc(sizeof(Toy));
superman->makeSound = supermanSound; // Assigns Superman's sound function
return superman;
}
// Function to create a Barbie toy
// Uses dynamic polymorphism by assigning the appropriate sound function to the function pointer
Toy* createBarbie() {
Toy* barbie = (Toy*)malloc(sizeof(Toy));
barbie->makeSound = barbieSound; // Assigns Barbie's sound function
return barbie;
}
int main(void) {
// Create toy instances using factory functions
Toy* barbie1 = createBarbie();
Toy* barbie2 = createBarbie();
Toy* superman1 = createSuperMan();
// Array of toy pointers
Toy* toys[] = { barbie1, barbie2, superman1 };
// Loop through the toys and make them sound
// Dynamic polymorphism allows us to treat all toys uniformly
// without needing to know their specific types
for (int i = 0; i < 3; i++) {
makeSound(toys[i]);
}
// Free allocated memory
free(barbie1);
free(barbie2);
free(superman1);
return 0;
}
By using a function pointer within the Toy struct, the code can easily accommodate new toy types without modifying the core logic.
:::info
Factory function is a design pattern used to create objects without specifying the exact class or type of the object that will be created. In the context of C programming and especially in embedded software, a factory function helps in managing the creation and initialization of various types of objects (e.g., sensors, peripherals) in a modular and flexible manner. This pattern is particularly useful when dealing with dynamic polymorphism, as it abstracts the instantiation logic and allows the system to treat objects uniformly
:::
\
:::info
A function pointer is a variable that stores the address of a function, allowing the function to be called through the pointer. This enables dynamic function calls, which are particularly useful when the function to be executed needs to be determined at runtime.
Defining a Function Pointer
A function pointer is defined using the following syntax:
<return type> (*<pointer name>)(<parameter types>);
Example
For example, to declare a pointer to a function that returns an int and takes two int parameters, you would write:
int (*functionPointer)(int, int);
:::
\
Welcome to Billionaire Club Co LLC, your gateway to a brand-new social media experience! Sign up today and dive into over 10,000 fresh daily articles and videos curated just for your enjoyment. Enjoy the ad free experience, unlimited content interactions, and get that coveted blue check verification—all for just $1 a month!
Account Frozen
Your account is frozen. You can still view content but cannot interact with it.
Please go to your settings to update your account status.
Open Profile Settings