About OOP
Blog address: http://www.cnblogs.com/ronny reprint please indicate the source!
1. inheritance can be a single inheritance or multiple inheritance. Each inheritance connection can be public, protected, or private, or virtual or non-virtual.
2. Each option of the member function is virtual, non-virtual, or pure-virtual.
3. Interaction between member functions and other language features: What are the interactions between default parameter values and virtual functions? How does inheritance affect the name search rules of C ++? What are the design options? If the class behavior needs to be modified, is the virtual function the best choice?
4. public inheritance means "is-".
5. virtual functions mean that "interfaces must be inherited", and non-virtual means that "interfaces and implementations must be inherited ".
Clause 32: Determine whether your public inherits the is-a relationship.
"Public inheritance" means is-. Everything that applies to base classes must also be suitable for derived class, because each derived classes object is also a base classes object.
The above relationship sounds simple, but sometimes your intuition may mislead you. For example, it is a fact that penguins are a bird. It is also true that birds can fly. However, if the penguin is defined as the public inheritance of birds when class descriptions are used, a problem may occur, for example, defining the fly function for the base class.
Cla33: Avoid hiding the inherited name
The names in derived classes mask the names in base classes. Under public inheritance, no one ever wants to do anything like this.
// Blog address: http://www.cnblogs.com/ronny/ reprint please indicate the source! Class Base {private: int x; public: virtual void mf1 () = 0; virtual void mf1 (int); virtual void mf2 (); void mf3 (); void mf3 (double) ;}; class Derived: public Base {public: virtual void mf1 (); // blocks mf1 (int) void mf3 () in the Base (); // The mf3 void mf4 () ;}in the Base is blocked ();};
Derived d; int x; d. mf1 (); // OK, call Derived: mf1d. mf1 (x); // error, because Derived: mf1 hides Base =: mf1d. mf2 (); // OK, call Base: mf2d. mf3 (); // OK, call Derived: mf3d. mf3 (x); // error, because Derived: mf3 hides Base: mf3
The functions in Derived cover all functions with the same name in the Base. Of course, if the implementation is a kind of public inheritance, it is necessary to inherit the overload function in the same way, because it must conform to the is-a relationship.
We can achieve our goal with using declarative:
class Derived :public Base{public: using Base::mf1; using Base::mf3; virtual void mf1(); void mf3(); void mf4();};
Derived d; int x; d. mf1 (); // OK, still calling Derived: mf1d. mf1 (x); // No problem now d. mf2 (); // OK, call Base: mf2d. mf3 (); // OK, still calling Derived: mf3d. mf3 (x); // No problem
We can also use the exchange function to accomplish this function: Sometimes we don't want to inherit all the functions of base classes.
Class Derived: private Base {public: virtual void mf1 () // Transfer Function {Base: mf1 ();}};
Cla34: differentiate interface inheritance and implement inheritance
Interface inheritance and implementation inheritance are different. Under public inheritance, derived classes always inherits the base class interface.
The pure virtual function only specifies the interface inheritance
Simple (non-pure) impure virtual functions specify interface inheritance and default implementation inheritance.
Non-virtual functions specify interface inheritance and mandatory implementation inheritance
Clause 35: consider other options than virtual functions
These terms talk about two design patterns and encourage us to think more.
Taking the character design inheritance system in the game as an example, different characters have different health indexes calculation methods. It is called the healthValue function. Naturally, we will think of designing a base class, the healthValue function is designed as virtual for inheritance.
These terms provide two different ideas for virtual substitution:
1. Template Method mode,Non-Virtual InterfaceMethod implementation.
The preceding example is as follows:
class GameCharacter {public: int healthValue() const { // ... int retVal = doHealthValue(); // ... return retVal; }private: virtual int doHealthValue() const { }};
In fact, the first Method to design healthValue as virtual is also the Template Method mode. Here, healthValue is kept as public and implemented as private virtual (of course, it can be protected ), the advantage of this is that "... "(ellipsis), which can be used for similar checks and adjustments to ensure that doHealthValue () is called in an appropriate scenario. In addition, sub-classes can inherit and implement private virtual member functions.
2. Strategy ModeThis pattern makes the implementation method a variable, and even the same object can have different implementation methods in different time periods. But there is a constraint here, that is, access to private member variables.
A) Function Pointers implementation: the constraint of this implementation method is that it can only be a Function, and the form is restricted by the Function signature (parameter quantity, parameter type, return type.
B) tr1: function implementation, get rid of a) constraints, support implicit type conversion, and support function objects or member functions (implemented through std: tr1: bind)
C) Classical implementation is actually the object entity, while the implementation method is the other.
Cla36: Do not redefine the inherited non-virtual function
When a non-virtual function in the base class is redefined in a derived class, if you use a pointer to the base class type (actually pointing to the derived class Object) to access the non-virtual function, the non-virtual function of the base class object is accessed.
The reason for the above behavior is that non-virtual is statically bound, which is different from dynamic binding of virtual functions.
Clause 37: Do not redefine the inherited default parameter values
Virtual functions are dynamically bound, while default parameter values are statically bound.
Static object type: the type used when the object is declared in the program. The dynamic type of an object refers to the type of the object currently referred to. That is to say, the dynamic type can show the behavior of an object.
class Shape{public: enum ShapeColor{ Red, Green, Blue }; virtual void draw(ShapeColor color = Red)const = 0;};class Rectangle :public Shape{public: virtual void draw(ShapeColor color = Green)const = 0;};
In the above Code, if we define a pointer of type Shape * to point to Rectangle.
Shape * ps = new Rectangle;
Ps-> draw ();
Our actual desire may be to set the default parameter of draw to Green, but it is actually Red, because the default parameter is statically bound.
You may want to make the virtual functions in the derived class consistent with those in the base class. But the following code is obviously not feasible.
class Shape{public: enum ShapeColor{ Red, Green, Blue }; virtual void draw(ShapeColor color = Red)const = 0;};class Rectangle :public Shape{public: virtual void draw(ShapeColor color = Red)const = 0;};
The above will not only produce code duplication, but also bring about dependency (if the default parameter in the Shape changes, the default value of all the virtual functions of the derived classes must be modified ).
The smart approach is to consider alternative design, such as the alternative design of some virutal functions in cla35, one of which is the NVI method, make a public non-virtual function in the base class call the private virtual function.
class Shape{public: enum ShapeColor{ Red, Green, Blue }; virtual void draw(ShapeColor color = Red)const { doDraw(color); }private: virtual void doDraw(ShapeColor color)const = 0;};class Rectangle :public Shape{private: virtual void doDraw(ShapeColor color)const;};
Clause 38: Use composite molding to produce has-a or "based on something"
Combination or inclusion means has-. If we want to design our own set, we think we can use list to implement it, but if I design it as a list derived class, there will be problems, because all the behaviors of the parent class are allowed in the derived class, while the list allows repeated elements, but the set obviously does not, the is-a relationship between the set and list does not match, we can design the list as a member of the set, that is, the inclusion relationship (has-).
Cla39: use private inheritance with knowledge and prudence
Private inheritance means is-implemented-in-terms of (implemented based on something ). It is generally lower than the composite level. However, this design is reasonable when the derived class needs to access members of the protected base class or to redefine the inherited virtual function.
Unlike compound, private inheritance can optimize empty base. This may be important for library developers who are committed to minimizing object sizes.
class Empty{};class HoldsAnInt{private: int x; Empty e;};
At this time, you will find that sizeof (HoldsAnInt)> sizeof (int), because of some memory alignment requirements.
If:
class HoldsAnInt :private Empty{private: int x;};
At this time, we can almost be certain: sizeof (HoldsAnInt) = sizeof (int ).
Clause 40: wise and prudent use of multi-Inheritance
Ambiguity should be considered when multiple inheritance is used (the names of member variables or member functions are duplicated ).
The simplest solution is explicit calling (such as the form of item. Base: f ).
If it is more complex,"Diamond multi-InheritanceTake File as an example:
class File { ... }class InputFile : public File { ... }class OutputFile : public File { ... }class IOFile : public InputFile, public OutputFile { ... }
The problem here is that when the File has a filename, both InputFile and OutputFile are available. After the IOFile is inherited, it will be copied twice, and there will be two filename, this is logically inappropriate. The solution is to useVirtual inheritance:
class File { ... }class InputFile : virtual public File { ... }class OutputFile : virtual public File { ... }class IOFile : public InputFile, public OutputFile { ... }
In this way, only one copy of the data shared by InputFile and OutputFile will be retained in IOFile.
However, virtual inheritance is not commonly used because:
1. virtual inheritance will increase the cost of space and time.
2. virtual inheritance is very complex (write cost), because both indirectly and directly inherited virtual base classes must initialize these bases, regardless of the number of layers of inheritance.
Remember
Multi-inheritance is more complex than single inheritance. It may lead to new ambiguities and the need for virtual inheritance.
Virtual inheritance increases the costs of size, speed, initialization (assignment), and so on. If virtual base classes does not contain any data, it will be the most useful.
Multi-inheritance does have a legitimate purpose. One of the plots involves the combination of "public inheriting an Interface class" and "private inheriting a class that is assisted in implementation.