As a class designer, sometimes you want the derived class to inherit only the interfaces (that is, declarations) of the member functions: Sometimes you want the derived class to inherit the functions at the same time.
Interfaces and implementations, but also want to overwrite (override) their inherited implementations: And sometimes you want the derived class to inherit the function interfaces and implementations at the same time, it is not allowed to overwrite anything.
Let's consider a class inheritance system that shows various geometric shapes in a drawing program:
Class shape {
Public:
Virtual void draw () const = 0;
Virtual void error (const STD: string & MSG );
Int objectid () const;
};
Class rectangle: Public shape {...};
Class ellipse: Public shape {...};
SHAPE's pure virtual function draw makes it an abstract class. Shape strongly affects all derived classes that inherit it in the public form. Because:
The interfaces of member functions are always inherited.
First, consider that the pure virtual function draw: pure virtual function has two outstanding features: they must be re-declared by any concrete class that "inherits them, they are generally not defined in abstract classes. Therefore:
The purpose of declaring a pure virtual function is to allow the derived class to inherit only the function interface. Shape: the declarative form of draw is to say "you must provide a draw function, but I will not interfere with how you implement it"
Unexpectedly, we can provide definitions for the pure virtual function. However, the only way to call it is to explicitly specify its class name during the call ":
Shape * PS = new shape; // wrong! Abstract class shape
Shape * PS1 = new rectangle;
PS1-> draw (); // rectangle: Draw ()
Shape * PS2 = new ellipse;
PS2-> draw (); // ellipse: Draw ()
PS1-> shape: Draw ();
PS2-> shape: Draw ();
It provides a mechanism to provide more secure default implementations for simple impure virtual functions.
The impure virtual function provides an implementation code. The Derived classes may overwrite it:
The objective of a simple (non-pure) impure virtual function is to let the derived class inherit the interface and default Implementation of the function.
Consider the shape: Error example.
The interface indicates that each class must support a function called when an error occurs, but each class can handle the error freely. If a class does not want to take any special actions against errors, it can return to the default error handling behavior provided by shape class.
However, it is dangerous to allow impure virtual functions to specify both the function declaration and default function behavior. Consider the inheritance system designed by XYZ airlines. The company only has two types of aircraft a and B, which are flying in the same way:
Class airport {...};
Class airplane {
Public:
Virtual void fly (const airport & Destination );
...
};
Void airplane: Fly (const airport & Destination)
{
// Default code to fly an airplane to a specified destination
}
Class modela: Public airplane {...};
Class modelb: Public airplane {...};
In order to indicate that all planes must fly, and clarify that "different aircraft require different fly implementations in principle" airplane: Fly is declared as virtual. To avoid writing the same code in modela and modelb, the default flight behavior is provided by airplane: fly, which is inherited by modela and modelb at the same time.
Today, XYZ has decided to purchase a new type of C aircraft, which has different flight modes.
XYZ programmers added a class to the C aircraft in the inheritance system, but they forgot to redefine the fly function because they were eager to bring the new aircraft into service:
Class modelc: Public airplane {...};
Then there are some actions like this in the Code:
Airport PDX (...);
Airplane * pA = new modelc;
...
Pa-> fly (PDX );
This will lead to a disaster: This program tries to fly modelc using modela and modelb.
The problem is not that airplane: Fly () has a default behavior, but that modelc inherits this default behavior if it does not understand "I want. We can achieve "provide the default implementation to derived classes, but it is not necessary unless they are explicitly required ". The trick is to disconnect the connection between the "virtual function interface" and its "Default implementation. The following is a method:
Class airplane {
Public:
Virtual void fly (const airport & Destination) = 0;
...
Protected:
Void defaultfly (const airport & Destination );
};
Void airplane: defaultfly (const airport & Destination)
{
// Default action: fly the plane to the destination
}
Fly has been changed to a pure virtual function, which only provides flight interfaces. The default behavior is ultfly in airplane class. If you want to use the default implementation (such as modela and modelb), you can make an inline call to defaultfly in Fly:
Class modela: Public airplane {
Public:
Virtual void fly (const airport & Destination)
{
Defaultfly (destination );
}
...
};
Class modelb: Public airplane {
Public:
Virtual void fly (const airport & Destination)
{
Defaultfly (destination );
}
...
};
Currently, modelc cannot inherit the incorrect fly implementation code by mistake, because the pure virtual function in airplane forces modelc to provide its own fly version:
Class modelc: Public airplane {
Public:
Virtual void fly (const airport & Destination );
...
};
Void modelc: Fly (const airport & Destination)
{
// Fly the C aircraft to the specified destination
}
This solution is not safe. programmers may still be troubled by the copy-and-paste code, but it is worth relying on than the original design.
Airplane: defaultfly is a non-virtual function, which is also important. Because no derived class should redefine this function.
Some people are opposed to providing interfaces and default implementations with different functions, like fly and defaultfly above. We can use the fact that "pure virtual functions must be re-declared in derived class, but they can have their own implementations. The following is how the airplane inheritance system defines the pure virtual function:
Class airplane {
Public:
Virtual void fly (const airport & Destination) = 0;
...
};
Void airplane: Fly (const airport & Destination) // implement the pure virtual function
{
// Default action: fly the plane to the specified destination
}
Class modela: Public airplane {
Public:
Virtual void fly (const airport & Destination)
{
Airplane: Fly (destination );
}
...
};
Class modelb: Public airplane {
Public:
Virtual void fly (const airport & Destination)
{
Airplane: Fly (destination );
}
...
};
Class modelc: Public airplane {
Public:
Virtual void fly (const airport & Destination)
...
};
Void modelc: Fly (const airport & Destination)
{
// Fly the C aircraft to the specified destination
}
This is almost the same as the previous design, except that the pure virtual function airplane: Fly replaces the independent function airplane: defaultfly. The declared part of the table represents the interface (which must be used by the derived class), and the defined part of the table represents the default behavior (which may be used by the derived class, but only when they explicitly apply ). Merging fly and defaultfly will lead to the loss of the opportunity to "give two functions different protection levels": the function that is used to being set as protected (defaultfly) now it becomes public (because it is in Fly ).
If a member function is a non-virtual function, it means that it does not intend to have different behaviors in Derived classes. Non-virtual member functions present immutability over their specificity. No matter how special the derived class is, its behavior cannot be changed.
The purpose of declaring a non-virtual function is to make the derived class inherit the function interface and a mandatory implementation.
Let's look at the shape: objectid statement: you can think of it as "every shape object has a function used to generate an object identifier: This identifier always uses the same calculation method, which is composed of shape :: the definition of objectid determines that no derived class should try to change its behavior ".