The concept of public inheritance is composed of 2 parts: function interface (functions Interface) inheritance and function implementation inheritance. The differences between the two types of inheritance are somewhat like the difference between the declaration of a function and the definition of a function.
When we design class, we sometimes want the derived class to inherit only the interface of the function (that is, the function declaration); Sometimes you want derived class to inherit the function interfaces and implementations, but also to overwrite the implementations they inherit; sometimes I hope derived Class inherits both the interface and the implementation of the function, but does not overwrite anything.
To better understand the above differences, use a drawing program to illustrate:
class Shape{ public: virtualvoiddrawconst=0; virtualvoid error(const std::string& msg); intconst; …… }; public Shape{……}; class Ellipse:public Shape{……};
There is a pure virtual function in shape, so it is an abstract class and cannot create a Shape object, but shape strongly affects all derivedclass that inherit it with public because
-the interface of the member function is always inherited. The term "public inheritance" means is-a.
The Shape class has three functions. is the draw
pure virtual function; It is the error
impure pure function; it is the objectID
non-virtual function.
The pure virtual function has two characteristics: they must be re-declared with their specific class, and are usually not defined in the abstract class. This is also the explanation:
- The purpose of declaring a pure virtual function is to have derived class inherit only the function interface.
This is also reasonable, because shape::d Raw does not know what images we are going to draw, of course we cannot give the implementation. But we can provide a definition for the pure virtual function, which is to Share::draw
provide a copy of the implementation, C + + does not complain, but the only way to call this function is to indicate its class name at the time of invocation:
Shape* ps=new Shape; ps->draw(); ps->Share::draw();
The impure virtual function differs from the pure virtual function, derived classes inherits its function interface, but the impure virtual function provides an implementation code that derived class may overwrite (override).
- The purpose of declaring a simple (non-pure) impure virtual function is to have derived classes inherit the interface and default implementation of the function.
Considering the example of Shape::error, the error interface indicates that each class must support a function that can be called when an error is encountered, but each class is free to handle the error. If a class does not want to make a special behavior for the error, it can revert back to the default error-handling behavior provided by the shape class. That is to say Shape::error's declarative tells derived class Designer: You must support an error function, but if you do not want to write it yourself, you can use the default version provided by Shape class.
If the impure virtual function is allowed to specify both function declaration and function default behavior, it can be dangerous. Consider a specific example of an XYZ airline's design of an aircraft succession system, which has both A and B types of aircraft, all flying in the same way, and may consider such a design inheritance system:
class Airport{......}; class airplane{ Public: VirtualvoidFlyConstairport& destation); ...... };voidAirplane::fly (Constairport& destation) {//Fly the aircraft to the designated destination} class modela: public airplane{......}; class modelb: public airplane{......};
Because different planes do not require different fly implementations, Airplane::fly
they are declared virtual; To avoid re-composing the same code in Modela and MODELB, the default flight behavior is Airplane::fly
provided.
The above design approach is a typical object-oriented design. The properties of the two classes share are placed in the base class and then inherited by the two class. This can highlight the common nature and avoid duplication of code.
But if XYZ wants to buy a new type of aircraft c,c and a, b fly differently. XYZ company programmer added a class to the C-plane, but did not redefine the fly function
class ModelC: public Airplane{……};
And then wrote the following code
Airport PDX();//某个机场 Airplane* pa=new ModelC; …… pa->fly(PDX);//调用了Airplane::fly
This can cause a catastrophe, as programmers try to fly MODELC in Modela or Modelb ways. The problem is not that Airplane::fly has a default behavior, but that MODELC uses this default behavior when it is not clear. Fortunately, you can do this by providing a default implementation to derived classes, but unless derived classes is really used. This is done by cutting off the connection between the virtual function interface and its default implementation.
class Airplane{ public: virtualvoidfly(const Airport& destation)=0; protected: voiddefaultFly(const Airport& destation); }; void Airplane::deFaultFly(const Airport& destation) { //将飞机飞到指定目的地 }
This changes the airplane::fly to the pure virtual function, providing only the interface. However, the default behavior occurs in the airplane::d efaultfly function. If you want to use its default behavior, you can call the Defaultfly function in the fly function.
class Modela: public airplane{ Public : virtual void fly (const airport& destation) {defaultfly (destation)}}; Class Modelb: public airplane ... class MODELC: public airplane{public : virtual void fly (const airport& destination); }; void Modelc:fly (const airport& destination) {//will fly type C The machine flies to the specified destination }
The above design, Airplane::defaultFly
is a non-virtual,derived classes without redefining (* * clause **36). Airplane::defaultFly
in the case of the virtual function, there is a looping problem: what if derived classes forgets to redefine the Defaultfly function?
Some people return with different functions respectively will provide interface and default implementation, this will cause the class namespace pollution problem because of the excessive similarity of function name, but they agree that the interface and the default implementation should be separated. We can take advantage of the "pure virtual function must be re-declared in derived classes, but they can have their own implementation" this feature
Class airplane{ Public:Virtual void Fly(Constairport& destination) =0; ...... };voidAirplane::fly (Constairport& destination)//pure virtual function Implementation{//default implementation} Class Modela: Publicairplane{ Public:Virtual void Fly(Constairport& destination) {airplane::fly (destination);} ...... }; Class Modelb: PublicAirplane ...classMODELC: PublicAirplane { Public:Virtual void Fly(Constairport& destination); ...... };voidModelc::fly (Constairport& destination) {the realization of//MODELC}
This implementation differs from the previous one in that the standalone function airplane is replaced with the pure virtual function Airplane::fly::d efaultfly. The fly is now divided into two basic elements: its declaration is partially represented as an interface (derived classes must be used), and the definition is partially represented by the default behavior (derived classes explicitly applies).
Finally, look at shape's non-virtual function objectid; it Shape::objectID
's a non-virtual function, which means it's not going to behave differently in derived class.
- The purpose of declaring the Non-virtual function is to make derived classes the interface of the inherited function and a mandatory implementation.
You can think of shape::objectid as "every Shape object has a function to generate the identification code, which uses the same calculation method." The meaning of the non-virtual function is that invariance (invariant) overrides specificity (specialization), so it should not be redefined in derived classes, and this * * clause **36 the focus of the discussion.
The pure virtual function corresponds to only the inherited interface; simple (impure) virtual function corresponds to the inheritance interface and a default implementation; The Non-virtual function corresponds to an inheritance interface and a mandatory implementation. In the design of classes, to distinguish between these differences and contact, otherwise prone to make two errors:
- The first error is to declare all functions as non-virtual. This causes derived classes to have no space for special work; the Non-virtual function has a problem with the destructor ( clause **7). If you are concerned about the cost of the virtual function, you can refer to the 80-20 rule of clause **30. A typical program has 80% time to execute 20% code, the function of 80% of the virtual function does not necessarily give the program a lot of efficiency loss, the effort to spend on 20% code is the key.
- The second error is to declare all member functions as virtual. Sometimes this is correct, for example * * clause **31 interface classes. If some functions should not be redefined in derived classes, then these functions should be declared as non-virtual.
Summarize
- Interface inheritance differs from implementation inheritance. Under public inheritance, derived classes always inherits the interface of base classes.
- The pure virtual function specifies only interface inheritance.
- The impure virtual function specifically specifies interface inheritance and default implementation inheritance.
- The Non-virtual function specifies interface inheritance and mandatory implementation inheritance.
"Effective C + +": Clause 34: Differentiate between interface inheritance and implementation inheritance