"Effective C + +" inheritance and object-oriented design

Source: Internet
Author: User

About OOP

1, inheritance can be a single inheritance or multiple inheritance, and each inherited connection can be public, protected, or private, or virtual or non-virtual.

2, the individual options for the member function: virtual or non-virtual or pure-virtual.

3, interaction of member functions and other language features: what are the interaction effects of default parameter values with the virtual function? How does inheritance affect the name lookup rules of C + +? What are the design options? If the behavior of class needs to be modified, is the virtual function the best choice?

4,public inheritance means "is-a".

The 5,virtual function means that "interfaces must be inherited" and non-virtual means that "interfaces and implementations must be inherited".

Clause 32: Determine your public inheritance mold out is-a relationship

"Public inheritance" means is-a. Every thing that applies to base classes must also be appropriate for derived class, because each derived classes object is also a base classes object.

The relationship above sounds quite simple, but sometimes your intuition may mislead you, for example, a penguin is a bird, which is true. Birds can fly, which is also true, but if you define a penguin as a public inheritance of a bird when you use a class description, it can be problematic, such as defining a fly function for a base class.

Article 33: Avoid hiding the inherited name

The name within the derived classes will obscure the name within the base classes. No one has ever wanted to be like that under public succession.

ClassBase{Private:IntXPublic:virtual voidMF1 () = 0;virtual voidMF1 (Int);virtual voidmf2 (); void mf3 (); void double);}; class derived :public base{public: virtual void mf1 (); //shielded the MF1 (int) void MF3 ( ); //shielded the mf3 void mf4 ();};   
D  x;d.mf1 ();    //ok, call Derived::mf1//error, because Derived::mf1 obscures base=::mf1//ok, calls Base::mf2//ok, calls Derived::mf3  Error, because DERIVED::MF3 hides base::mf3.     

The functions inside the derived obscure all functions of the same name in base. Of course, if it is a public inheritance, it is necessary to inherit the overloaded function similarly, because the is-a relationship must be met.

We can achieve the goal with a using declarative formula:

class derived :public base{publicBASE::MF1; Span style= "color:blue;" >using BASE::MF3; virtual void mf1 (); void mf3 (); void mf4 ();};        
Derived D;  x;d.mf1 ();    //ok, still calls DERIVED::MF1d.mf1 (x);    //Now No problem //ok, call Base::mf2//ok, still call derived::mf3//No problem anymore      

We can also use the SWAP function to do this: sometimes we don't want to inherit all the functions of base classes.

:base{publicbase:: Mf1 ();}};    
Article 34: Differentiating between interface inheritance and implementation inheritance

Interface inheritance differs from implementation inheritance. Under public inheritance, derived classes always inherits the interface of base class.

Pure virtual functions Specify interface inheritance only

Simple (non-pure) impure virtual functions Specify interface inheritance and default implementation inheritance.

non-virtual functions Specify interface inheritance and enforce inheritance

Article 35: Consider alternatives other than the virtual function

This article talks about two design patterns that encourage us to think more.

In the game of the character design inheritance system For example, different people have different methods to calculate health index, called Healthvalue function bar, it is natural to think of a base class, the Healthvalue function is designed for virtual inheritance.

This article provides two different ideas for virtual alternatives:

1. Template method mode , implemented by non-virtual Interface technique.

The above example is probably as follows:

{public:    retVal;} private{}};    

In fact, the first mention of the Healthvalue design for virtual is also the template method mode, and this is reserved healthvalue for public, and implemented as private Virtual (of course, here is protected), the advantage is that the "..." (ellipsis), this part can do some similar check, adjust the operation, to ensure that Dohealthvalue () in a suitable scenario for the downgrade. And subclasses can also inherit the private virtual member function.

2. Strategy mode , this mode makes the implementation method a variable, even if the same object at different times can have a different implementation method. But there is a constraint here, which is the restriction of access to private member variables.

A) function pointers implementation, the constraints of this implementation method can only be functions, and the form by the signature of the function (number of parameters, parameter type, return type) constraints.

b) tr1::function implementation, get rid of a) constraints, support implicit type conversions, also support function objects or member functions (implemented by Std::tr1::bind)

c) The classical realization, is actually the object entity is one kind, but the realization method is another kind.

Article 36: Never redefine inherited non-virtual functions

When a non-virtual function in a base class is redefined in a derived class, accessing the Non-virtual function with a pointer to a base class type, which actually points to a derived class object, accesses the Non-virtual function of the base class object.

The reason for this behavior is that non-virtual is statically bound and differs from the dynamic binding of the virtual function.

Article 37: Never redefine inherited default parameter values

The virtual function is dynamically bound, and the default parameter value is statically bound.

The static type of a so-called object: the type to be used when declared in a program. The dynamic type of the object refers to the "type of the object currently referred to", that is, the dynamic type can show what behavior an object will have.

ClassShape{Public:EnumShapecolor{Red,Green,Blue};virtual void  Shapecolor red" Span style= "color:blue;" >const = 0;}; class rectangle :public shape{public: virtual void draw ( Shapecolor green" Span style= "color:blue;" >const = 0;};            

In the above code, if we define a pointer of type shape*, point to rectangle.

shape* ps=new Rectangle;

Ps->draw ();

Our real desire might be to have the default parameter for draw green, but it is actually red, because the default parameter is statically bound.

Perhaps you might want to keep the virtual function in the derived class consistent with the base class. But the following code is obviously not feasible.

ClassShape{Public:EnumShapecolor{Red,Green,Blue};virtual void  Shapecolor red" Span style= "color:blue;" >const = 0;}; class rectangle :public shape{public: virtual void draw ( Shapecolor red" Span style= "color:blue;" >const = 0;};            

The above will not only generate code duplication, but also bring dependencies (if the default parameters within shape change, all derived classes of the virtual function default values are modified).

It is wise to consider alternative designs, such as alternative designs for some of the virutal functions in clause 35, one of which is the NVI technique, which calls the private virtual function of a public non-virtual function within the base class.

ClassShape{Public:EnumShapecolor{Red,Green,Blue};virtual voidDrawShapecolorColor=Red)Const{Dodraw (color); privatevirtual void dodraw (shapecolor color) = 0;}; class rectangle :public shape{private: virtual void dodraw ( Shapecolor const  
Article 38: Has-a by Composite molding or "by something"

A compound or inclusion means has-a. If we want to design a set of our own, we think that we can use list to implement it, but if I design it out of a list of a derived class, there will be a problem, because all the behavior of the parent class is allowed in the derived class, and the list allows elements to repeat, and set is obviously not, So the set and list do not conform to the is-a relationship, we can design the list as a member of the set, that is, the inclusion relationship (HAS-A).

Article 39: Knowingly and prudently uses private inheritance

Private inheritance means is-implemented-in-terms of (according to something). It is usually lower than the compound level. However, this design is reasonable when derived class needs to access the members of the protected base class or need to redefine the inherited virtual functions.

Unlike compounding, private inheritance can result in the optimization of empty base. This may be important for library developers who are committed to minimizing the size of objects.

Empty{};  Holdsanint{privatee;};    

At this point you will find sizeof (Holdsanint) >sizeof (int) Because of some memory alignment requirements.

And if it is:

:empty{privatex;};  

This is almost certain: sizeof (holdsanint) ==sizeof (int).

Article 40: Use multiple inheritance wisely and prudently

Using multiple inheritance takes into account ambiguity (the name of a member variable or member function).

The simplest scenario is a solution that is explicitly called (such as item. The form of Base::f ().

More complex, there may be " Multiple inheritance of diamond type ", taking file as an example:

class file {...} class inputfile : public file {...} class outputfile : public file {...} class iofile public inputfilepublic Span style= "color: #2b91af;" >outputfile 

The problem here is that when file has a filename, inputfile and outputfile are there, then Iofile will replicate two times, there are two filename, which is logically inappropriate. The solution is to inherit with virtual :

Classfile {...} class inputfile : virtual public file {...} class outputfile : virtual public file {...} class iofile public inputfilepublic Span style= "color: #2b91af;" >outputfile 

This way, the data shared with OutputFile will be inputfile in Iofile.

However, virtual inheritance is not commonly used because:

1. Virtual inheritance increases the cost of space and time.

2. Virtual inheritance can be very complex (writing costs) because the virtual base class, either indirectly or directly inherited, must assume the initialization of these bases, regardless of how many layers of inheritance are.

Please remember

Multiple inheritance is more complex than single inheritance. It can lead to new ambiguity and the need for virtual inheritance.

Virtual inheritance increases the cost of size, speed, initialization (Assignment) complexity, and so on. If virtual base classes does not carry any data, it will be the most useful case.

Multiple inheritance does have a legitimate purpose. One of the episodes involves the two-phase combination of "public inheriting a interface class" and "private inheriting a class of assistance implementations."

"Effective C + +" inheritance and object-oriented design

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.