Effective C + + reading notes (clause 35-40)

Source: Internet
Author: User

(vi). Inheritance and object-oriented design

_______________________________________________________________________________________________________________ _____________________

Article 35: Consider alternatives other than the virtual function

#1Four alternatives to the. Virual function:
(1)Using the Non-virtual Interface (NVI) method, it is the Template method design mode.
A special form. It wraps the lower access with the public Non-virtual member function (private
or protected) of the virtual function.
(2)Replace the virtual function with the function pointer member variable, which is a strategy design pattern
Decomposition of the expression mode.
(3)Replace the virtual function with the Tr1::function member variable, thus allowing any callable object to be used
(callable entity) with a demand-compatible signature. This is also the strategy design pattern
Some form of it.
(4)Replace the virtual function in the inheritance system with the virtual function in another inheritance system.
This is the traditional implementation of the strategy design pattern.

<1> The template method mode implemented by Non-virtual interface technique
Consider the following code: Class Gamecharacter{public:    int healthvalue () const             //derived classes do not redefine it    {                                    //See 36 clause        ...                                Do some pre-work, detailed under        int retVal = Dohealthvalue ();     Do the real work ...                                Do some post-mortem work    }    ... private:    virtual int dohealthvalue () const     //derived classes can redefine it    {        ...    }};/ /We call the Healthvalue function a virutal function dohealthvalue,//It provides a complete implementation, and the virtual function provides a concrete implementation,//through the NVi method, we can focus more on how the event is specifically done

<2>: Implementation of strategy mode by function pointers
Consider the following code: Class Gamecharacter; The predecessor Declaration (Forward Declaration)//The following function is the default algorithm for computing Health Index int DEFAULTHEALTHCALC (const gamecharacter& GC); class Gamecharacter    {public:typedef int (*healthcalcfunc) (const gamecharacter&); Explicit Gamecharacter (Healthcalcfunc HCF = Defaulthealthcalc): Healthfunc (HCF) {} int healthvalue () const {return h    Ealthfunc (*this);} ... private:healthcalcfunc healthfunc;};/ /different entities of the same character type can have different health calculation functions, for example: Class Evilbadguy:public gamecharacter{public:explicit evilbadguy (healthcalcfunc HCF =    DEFAULTHEALTHCALC): Gamecharacter (HCF) {...} ...};    int losehealthquickly (const gamecharacter&);        Health function Calculation function 1int losehealthslowly (const gamecharacter&);        Health function Calculation function 2EvilBadGuy ebg1 (losehealthquickly);        The same type of characters paired with Evilbadguy ebg2 (losehealthslowly); Different health computing methods//defualthealthcalc did not access the non-public ingredient of evilbadguy,//To access the non-public ingredient, you need to make it friends, so may//reduce the packaging of class. The advantages of using a function pointer to replace the virtual function, such as "each object//can have its own health calculation function" and "can change the calculation//function at run time", are sufficient to compensate for the disadvantage (for example, possible reduction of Gamecharacter//packaging), you have to choose according to the design situation. 

<3>: Completed strategy mode by Tr1::function
Consider the following code: Class Gamecharacter;int Defaulthealthcalc (const gamecharacter&); class Gamecharacter{public:// Healthcalcfunc can be any "callable" (callable entity),//can be called and accept anything compatible with Gamecharacter, returning anything//compatible with int.    Detailed below.    typedef std::tr1::function<int (const gamecharacter&) > Healthcalcfunc; Explicit Gamecharacter (Healthcalcfunc HCF = Defaulthealthcalc): Healthfunc (HCF) {} int healthvalue () const {Retu    RN Healthfunc (*this);} ... private:healthcalcfunc healthfunc;};/            /Today's tr1::function is equivalent to a generalization pointer to a function,//It has a more startling elasticity in the "Specify Health calculation function": Short calchealth (const gamecharacter&); Health calculation function//Note its return type is non-intstruct healthcalculator{/ /function object designed for computational health int operator () (const gamecharacter&) const {...}};    Class Gamelevel{public:float Health (const gamecharacter&) const;    member function to calculate health; ...//Note its non-int return type};class evilbadguy:public Gamecharacter{//Ibid.};                                                 Class Eyecandycharacter:public gamecharacter{//another character type; ...//assuming its constructor and};                    Evilbadguy same Evilbadguy ebg1 (calchealth); Figure 1, using a//function to calculate the health function Evecan        Dycharacter ecc1 (Healthcalculator ()); Figure 2, use a//function object to calculate health function Gamelevel CurrentLevel; Evilbadguy ebg2 (///function 3, use a std::tr1::bind (&gamelevel::health,//member function to calculate the health CurrentLevel, _1,)//See below);//The advantage of using tr1::function is to allow the use of any callable object,// As long as its invocation form is compatible with the signature

<4> By the classical strategy model
Consider the following code: Class Gamecharacter;class Healthcalcfunc{public: ...    virtual int Calc (CONST gamecharacter& GC) const    {...}    ...}; Healthcalcfunc Defaulthealthcalc;class gamecharacter{public:    explicit Gamecharacter (healthcalcfunc* PHCF = & DEFAULTHEALTHCALC)    : Phealthcalc (PHCF)    {}    int healthvalue () const    {return Phealthcalc->calc (* this); }    ... private:    healthcalcfunc* phealthcalc;};/ /The attraction of this solution is that it provides the possibility to "incorporate//use an existing health algorithm"---Just add a//derived class to the Healthcalcfunc inheritance system.
_______________________________________________________________________________________________________________ _____________________
Article 36: Never redefine inherited non-virtual functions
#1Never redefine inherited non-virtual functions
reasons (1): Non-virtual and redefined functions are statically bound,
The result of calling them is not what we expected.
Class B{public:    void Mf ();    ...}; Class D:public B {public:    void Mf ();    ...};D x; b* PB = &x;d* PD = &X;PB->MF ();        Call B::MF () PD->MF ();        Call D::MF ()//For the invocation of the same object x, there is a different behavior,//This is not what we want, and so redefined,//rather than let it become the virtual function
reasons (2)The inherited functions are divided into virtual and non-virtual, and virtual means
Its implementation is easily replaced, so dynamic binding is achieved, and non-virtual means that it is invariant
is above specificity and is applied to static bindings because it inherits its implementation.
_______________________________________________________________________________________________________________ _____________________
Article 37: Never redefine inherited default parameter values
#1Never redefine inherited default parameter values because the default parameter values are static bindings.
The virtual function---the only thing you should be able to overwrite---is dynamic binding.
A classclass shape{public:enum shapecolor {Red, Green, Blue} used to describe the geometry;    All shapes must provide a function to depict themselves as virtual void draw (shapecolor color = Red) Const = 0; ...}; Class Rectangle:public Shape{public://Note that different default parameter values are assigned.    Real bad!    virtual void Draw (shapecolor color = Green) const; ...};    Class circle:public shape{public:virtual void Draw (Shapecolor color) const//Notice that the above is written when the client calls this function with an object, be sure to specify the parameter value.    Because static binding sets this function, it does not inherit the default parameter values from its base.                        However, if you call this function as a pointer (or reference), you can not specify its parameter value,//Because dynamic binding sets this function to inherit the default parameter value from its base ...};//now consider the following pointer: shape* PS;            The static type is shape*shape* PC = new Circle;        Static type is shape*shape* PR = new Rectangle;            The static type is Shape*pc->draw (shape::red);            Call Circle::d Raw (shape::red) Pr->draw (shape::red); Call Rectangle::d Raw (shape::red)//The virtual function of this code is dynamic bound, and the default parameter value is static binding//This creates a strange way to call, not uniformly called, and the compiler// The reason for not dynamically binding for default parameter values is the run-time efficiency. So can we specify the same default value for derived? , just like this: class rectangle:public shape{public:virtual void Draw (Shapecolor color= Red) const; };//answer whether, the reason is very simple, this causes our code to repeat,//and with the dependency, if one day changes the default value will affect the whole body.    A better approach is to have non-virtual specify default values instead of work: Class Shape{public:enum Draw (shapecolor color = Red) const//Now it's a non-virtual    {Dodraw (color); Call a virtual} ... private:virtual void Dodraw (shapecolor color) const = 0;     Real work done here};class rectangle:public shape{public: ... private:virtual void Dodraw (shapecolor color) const;//No default value specified ...};//because the Non-virtual function is never written by the derived class, this design makes it clear that the value of the color default parameter of the//draw function is always true.
_______________________________________________________________________________________________________________ _____________________
Article 38: has-a by Composite molding or "by something"
#1Compound has a double meaning, in the application domain (application domains), compound means has-a
(There is one). In implementing domains (Implementation domain), compounding means
Is-implemented-in-terms-of (according to something).
_______________________________________________________________________________________________________________ _____________________
clause 39: Use private inheritance wisely and prudently
#1. Private inheritance means is-implemented-terms-of (implemented according to something). It
Usually lower than the level of composite (composition). But when derived class needs access
Protected the members of the base class, or the virtual functions that need to be redefined for inheritance
, this design is reasonable.
For example, we want to use the function in the Timer, we can implemented-in-terms-ofclass widget:private timer{private:                         //private permissions: Avoid customer calls    virtual void OnTick () const; Redefine the functionality of the Ontick function we need ...    };

#2A design alternative to private inheritance is to use the composite class, which is used as follows:
Class Widget{private:    class widgettimer:public timer{public    :        virtual void OnTick () const;        ...    };    Widgettimer timer;    };//the benefits of this design are://(1). It can have its own derived class.//(2). Minimize compilation dependencies (detach, dependent on declarative)

#3The size of an independent non-satellite object must not be zero, but as a secondary object, it exists
EBO (empty base optimization), the blank base class, is optimized to make it
Size is zero.
For example:
Class Empty{};class holdsanint:private empty{private:    int x;} Can almost be determined, sizeof (holdsanint) = sizeof (int)

#4Unlike composite (compresition), 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, when
#2的替代设计就不能再满足了.
_______________________________________________________________________________________________________________ _____________________
Article 40: Use multiple inheritance wisely and prudently
#1Virtual inheritance increases the cost of size, speed, and initialization, so do not use it when it is not necessary
Virtual inheritance. It also means that the less virtual base class data is, the more it uses
The higher the value, the more you should avoid placing the data in it.

#2When inheritance combinations are involved, multiple inheritance plays its rightful purpose. For example, "public inherits a
Interface class "and" private inherits a 22 combination of a help-implemented class ":
Class iperson{//The class indicates the need to implement Interface public:virtual ~iperson ();    Virtual std::string name () const = 0;    Virtual std::string birthDate () const = 0; Virtual std::string birthDate () const = 0;};  Class datebaseid{...}; Later used, the details are not important.    Class personinfo{//This class has several useful functions public://can be used to implement the IPerson interface.    Explicit PersonInfo (DatabaseID pid);    Virtual ~personinfo (();    Virtual const char* thename () const;    Virtual const char* thebirthdate () const;    Virtual const char* Valuedelimopen () const;    Virtual const char* Valuedelimclose () const; ...};    Class Cperson:public IPerson, private personinfo{//note, multiple inheritance public:explicit CPerson (DatabaseID pid):P ersoninfo (PID) {}    Virtual std::string name () const//implements the necessary IPerson function {return personinfo::thename ();} Virtual std::string birthDate () const//implements the necessary IPerson function {return personinfo::thebirthdate ();}    Private:const char* Valuedelimopen () const {return "";} Redefine inherited consT char* valuedelimclose () const {return "";} Virtual "boundary function"};

_______________________________________________________________________________________________________________ _____________________

Effective C + + reading notes (clause 35-40)

Related Article

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.