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 ". Unlike pure object-oriented programming languages, polymorphism in C ++ has a broader meaning. In addition to the common dynamic polymorphism that takes effect during runtime through class inheritance and virtual function mechanisms, templates also allow different special behaviors to be associated with a single generalized mark, this association is called static polymorphism because it is processed during compilation rather than runtime ).
In fact, macro and function overload mechanisms with variables also allow different special behaviors to be associated with a single generalized mark. In practice, however, we do not call the behavior they present as polymorphism (or static polymorphism ). Today, when talking about polymorphism, if it is not clearly indicated, it is dynamic polymorphism by default, while static polymorphism refers to template-based polymorphism. However, in this article titled C ++ polymorphism technology, we should first review another type of "polymorphism" that the C ++ community has long argued ": function polymorphism and, more often, macro polymorphism )".
Function Polymorphism
That is, the function overloading ). Based on different parameter lists, the same function name can point to different function definitions:
// Overload_poly.cpp
# Include <iostream>
# Include <string>
// Define two overloaded functions
Int my_add (int A, int B)
{
Return A + B;
}
Int my_add (int A, STD: String B)
{
Return A + atoi (B. c_str ());
}
Int main ()
{
Int I = my_add (1, 2); // Add two integers
Int S = my_add (1, & quot; 2 & quot;); // an integer is added to a string.
STD: cout <& quot; I = & quot; <I <& quot;/N & quot ;;
STD: cout <& quot; S = & quot; <S <& quot;/N & quot ;;
} & Nbsp;
Depending on the parameter list (type, number, or both), my_add (1, 2) and my_add (1, & quot; 2 & quot ;) compiled to call my_add (INT, INT) and my_add (INT, STD: string) respectively. The implementation principle is that the compiler reassembles the names of functions with the same name based on different parameter lists, and then these functions with the same name become different from each other. For example, a compiler may rename the my_add () function names to my_add_int_int () and my_add_int_str () respectively ().
Macro Polymorphism
Macros with variables can implement a primary form of static polymorphism:
// Macro_poly.cpp
# Include <iostream>
# Include <string>
// Define the generalized MARK: Macro add
# Define add (A, B) (A) + (B );
Int main ()
{
Int I1 (1), I2 (2 );
STD: String S1 (& quot; Hello, & quot;), S2 (& quot; World! & Quot ;);
Int I = add (I1, I2); // Add two integers
STD: String S = add (S1, S2); // two strings "add"
STD: cout <& quot; I = & quot; <I <& quot;/N & quot ;;
STD: cout <& quot; S = & quot; <S <& quot;/N & quot ;;
} & Nbsp;
When the program is compiled, the expressions add (I1, I2) and add (S1, S2) are replaced with two integers and the specific expressions of the two strings. The sum of integers is the sum, while the sum of strings is the join. The output result of the program is intuitive:
1 + 2 = 3
Hello, + world! = Hello, world! & Nbsp;
Dynamic Polymorphism
This is a well-known polymorphism. Modern object-oriented languages have consistent definitions of this concept. Its technical basis lies in the Inheritance Mechanism and virtual functions. For example, we can define an abstract base class vehicle and two specific classes car and airplane derived from vehicle:
// Dynamic_poly.h
# Include <iostream>
// Public abstract base class Vehicle
Class Vehicle
{
Public:
Virtual void run () const = 0;
};
// The Car class derived from vehicle
Class car: Public Vehicle
{
Public:
Virtual void run () const
{
STD: cout <& quot; run a car/N & quot ;;
}
};
// The specific airplane class derived from vehicle
Class airplane: Public Vehicle
{
Public:
Virtual void run () const
{
STD: cout <& quot; run a airplane/N & quot ;;
}
}; & Nbsp;
The customer program can manipulate a specific object by pointing to the pointer (or reference) of the base class vehicle. Calling a virtual function by pointing to a pointer (or reference) of a base class object will result in calls to the corresponding member of the specific object to be pointed:
// Dynamic_poly_1.cpp
# Include <iostream>
# Include <vector>
# Include & quot; dynamic_poly.h & quot;
// Run any vehicle through the pointer
Void run_vehicle (const vehicle * vehicle)
{
Vehicle-> Run (); // call the corresponding run () according to the specific type of vehicle ()
}
Int main ()
{
Car car;
Airplane airplane;
Run_vehicle (& amp; car); // call car: Run ()
Run_vehicle (& amp; airplane); // call airplane: Run ()
}
In this example, the key polymorphism interface element is the virtual function run (). Since the run_vehicle () parameter refers to the pointer to the base class vehicle, it is impossible to determine which version of run () to use during compilation (). During runtime, in order to assign function calls, the full dynamic type of the object called by the virtual function will be accessed. In this way, calling run_vehicle () for a car object actually calls car: Run (), while calling airplane: Run () for the airplane object ().
Perhaps the most attractive aspect of dynamic polymorphism is the ability to process heterogeneous object sets:
// Dynamic_poly_2.cpp
# Include <iostream>
# Include <vector>
# Include & quot; dynamic_poly.h & quot;
// Run the heterogeneous vehicles set
Void run_vehicles (const STD: vector <vehicle *> & amp; vehicles)
{
For (unsigned int I = 0; I <vehicles. Size (); ++ I)
{
Vehicles [I]-> Run (); // call the corresponding run () according to the specific vehicle type ()
}
}
Int main ()
{
Car car;
Airplane airplane;
STD: vector <vehicle *> V; // a set of heterogeneous vehicles.
V. push_back (& amp; car );
V. push_back (& amp; airplane );
Run_vehicles (V); // run different types of vehicles
}
In run_vehicles (), vehicles [I]-> Run () calls different member functions based on the type of elements being iterated. This shows the elegance of the object-oriented programming style.