About polymorphism, about C
The word polymorphism originally originated from the Greek polumorphos, meaning that it has many forms or forms. In the field of program design, a widely accepted definition is "an ability to associate different special behaviors with a single generalized mark ". However, in people's intuitive perception, polymorphism is equivalent to "the same method can make correct processing for different types of input parameters, and give people the expected results. "Perhaps this reflects people's expectations for the effect of polymorphism: make the program more intelligent and easy to use, designers are more and more able to see the essence of the problem that the code is going to reach through a variety of appearances.
As a reader, you may have profound insights on Object-Oriented Programming, or you may have a deep understanding of the convenience and magic of polymorphism. At this time, you began to question the difference: "polymorphism, that is only the technology of object-oriented programming. C language is process-oriented !" What I want to talk about is that C, as a programming language, may not be designed for Object-Oriented Programming, but it does not mean that it cannot implement the functions that object-oriented programming can implement, for example, polymorphism.
In this article, we use a simple single-chain table as an example to demonstrate how c Represents polymorphism.
Back to Top
Struct: a story to be told
Many programmers who begin to write C code and gradually move to C ++ know that the class in C ++ is actually the structure in C language. Many books that introduce C ++ Based on the C language background will clearly show you when introducing the class chapter, how does the structure in a C language gradually become a typical C ++ class? Finally, I come to the conclusion that "structure is a class that all members are public." Of course, class is still a class, and it cannot be simply regarded as a complicated structure.
Let's take a look at how a single-chain table node in C defines a simple storage of integer data, and of course it uses a struct. Most people will define in the linklist. h file like me:
Typedef struct node * linklist; struct node // linked list node {int data; // The stored Integer Data linklist next; // point to the next linked list node }; |
The linked list has some functions that you want to implement. Of course, they are defined as functions. We only provide several common functions:
Linklist initiallinklist (); // initialize link newlinklist (INT data); // create a new node void insertfirst (linklist H, int data ); // insert a node to the table header of an existing linked list. The operation void linklistoutput (linklist H); // output the data in the chain table to the console. |
These are a natural C Programming process. Then we can implement the above two functions in the linklist. c file and then call them in Main. C.
However, the linked list we defined above can only operate on Integer Data. If you want to use a chain table that stores strings next time, you have to repeat the process above. You may think that the process of slightly modifying the original code is not complicated, but we may constantly increase the operations on the data structure of the linked list, there are more and more data types that need to be stored using linked lists, which all mean massive code and tedious post-maintenance work. When you have hundreds of linked list structures that store different data types, the workload will increase to a disaster whenever you want to add an operation or modify the input parameters of an operation.
However, we can transform the above Code so that it can process any data type you want it to process: implementation, struct type, or any structure type you define.
Back to Top
Void *: Universal pointer "hook"
Almost all the teachers who teach the C language course will tell you: "pointers are the essence of the entire C language ." And you always respect the pointer, love and hate to use it. Many textbooks tell you that int * is a pointer to an integer, while char * is a pointer to the character type. However, there is an alternative pointer family member-void *. Do not call it a pointer to the void type according to the usual naming method. Its formal name is: it can point to any type of pointer. You must have noticed the word "any type". That's right. To achieve polymorphism, we rely on it.
Next we will transform our linked list code, in linklist. H, as follows:
Typedef struct node * linklist; struct node // linked list node {void * data; // The Stored Data Pointer linklist next; // point to the next linked list node}; linklist initiallinklist (); // initialize the Linked List link newlinklist (void * data); // create a new node void insertfirst (linklist H, void * data ); // insert a node to the table header of an existing linked list. The operation void linklistoutput (linklist H); // output the data in the chain table to the console. |
Let's take a look at the difference between the linked list and the one that can only store integer data.
When you define the members in the node struct as an integer data, it is like creating the linked list node into a box of fixed size and shape. You define a linked list node, when the program is compiled, the compiler will create a box for you: load an int type of data, and then install a linklist type pointer. If you want to forcibly install something in the box, the compiler will tell you, sorry, the size and shape of the box are not suitable. Therefore, you must create different production box pipelines for loading various types of data. to install the boxes of which types of data are required, start the corresponding pipeline for production.
However, when you define a struct member as void *, everything becomes different. At this time, the linked list node is no longer like a box of fixed size and shape, but more like a hook, it can be attached to any type of data. No matter what type of data you need to store, you only need to pass a pointer and store it in the node, which is equivalent to "hanging" the data, you can find it by pointer at any time. At this time, the linked list seems to have become a row of clothes hooks attached to the wall. You can have a row of coats, a row of hats, a row of scarves, or even, you can wear a coat, a hat, and a scarf on the wall. Void * first exposed, polymorphism is not far from the C language.
Back to Top
Implementation: you are the master of your Polymorphism
When you really start to do this, you will find that the operation of putting data into the linked list is no big difference with the implementation of a general int-type linked list, which is very convenient. But when you want to read the data that has already been stored, it is a little troublesome. For void * type pointers, the compiler only knows that it stores an address, but there is no concept about the data type in this address. After all, we can't expect the compiler to know everything and do everything for you, so the type of data stored in must be clearly known as a programmer and when we retrieve this data, use this type of pointer to forcibly convert void.
To facilitate this, I used a method to add a field that identifies the data type in the node struct and store the data types with an enumeration type. The linklist. H is as follows:
# Ifndef linklist_h # define linklist_h typedef struct node * linklist; Enum datatype {int, double, Char, string}; struct node // linked list node {void * data; // The Stored Data Pointer int datatype; // the stored data type linklist next; // points to the next linked list node}; linklist initiallinklist (); // initialize the linked list linklist newlinklist (void * data, int datatype); // create a new node void insertfirst (linklist H, void * data, int datatype ); // insert a node to the table header of an existing linked list. The operation void linklistoutput (linklist H); // output the data in the chain table to the console # endif |
Initialize the linked list using the following code:
linkList initialLinklist() { linkList link = (linkList*)malloc(sizeof(*link)); link->data = NULL; link->dataType = -1; link->next = NULL; return link; } |
Create a new node with the following code:
linkList newLinkList (void *data, int dataType) { linkList link = (linkList*)malloc(sizeof(*link)); link->data = data; link->dataType = dataType; link->next = NULL; return link; } |
Insert a node to the table header of an existing linked list. The Code is as follows:
void insertFirst(linkList h, void *data, int dataType) { linkList l = newLinkList(data, dataType); l->next = h->next; h->next = l; } |
The data in the output Link Table is sent to the console using the following code:
void linkListOutput(linkList h) { linkList p = h; p = p->next; while(p != NULL) { switch(p->dataType) { case 0: { printf("%4d", *(int*)(p->data)); break; } case 1: { printf("%4f", *(double*)(p->data)); break; } case 2: { printf("%4c", *(char*)(p->data)); break; } case 3: { printf("%s ", (char*)(p->data)); break; } } p = p->next; } printf("/n"); } |
Back to Top
Summary
Through the small example of the linked list above, you may have seen the flexibility of the C language. Although this code is short and simple, it has implemented polymorphism. The purpose of this article is not to tell everyone how to implement polymorphism in C, but the meaning of polymorphism is undoubtedly more extensive. The original intention of this article is actually based on this understanding: Object-oriented is a programming idea, while C is a programming language. Maybe it is not designed specifically for Object-Oriented Programming, but it does not mean it cannot implement object-oriented programming. Of course, the operations shown above can be completed in a few other programming languages, but what the C language wants to tell us is: Maybe I am not good at it, but it doesn't mean I can't do it.