Valid tive C ++ Reading Notes (terms 35-40), effective35-40

Source: Internet
Author: User

Valid tive C ++ Reading Notes (terms 35-40), effective35-40

(6) Inheritance and object-oriented design

____________________________________________________________________________________________________________________________________

Clause 35: consider other options than virtual functions

#1. Four alternatives to the virual function:
(1). Use the non-virtual interface (NVI) Method, which is the Template Method design mode.
. It uses the public non-virtual member function to package low access (private
Or protected.
(2) Replace virtual functions with "function pointer member variables", which is a design pattern of Strategy.
Decomposition mode.
(3). Replace the virtual function with the tr1: function member variable, so that any callable object can be used.
(Callable entity) with a signature that is compatible with requirements. This is also the design pattern of Strategy.
Some form.
(4). Replace the virtual functions in the inheritance system with the virtual functions in another inheritance system.
This is a traditional implementation method of the Strategy design model.

<1>. The Non-Virtual Interface Method is used to implement the Template Method mode.
// Consider the following code: class GameCharacter {public: int healthValue () const // derived classes does not redefine it {// see section 36... // do some work beforehand. For more information, see int retVal = doHealthValue (); // do the real work... // do some post-event work }... private: virtual int doHealthValue () const // derived classes can be redefined {...}}; // The healthValue function is called the doHealthValue overlay of the virutal function. // It provides a complete implementation, while the virtual function provides a specific implementation. // using the NVI method, we can focus more on how events are completed.

<2>. Implement the Strategy mode by Function Pointers
// Consider the following code: class GameCharacter; // forward declaration // The following function is the default Algorithm for calculating the health index: int defaultHealthCalc (const GameCharacter & gc ); class GameCharacter {public: typedef int (* HealthCalcFunc) (const GameCharacter &); explicit GameCharacter (HealthCalcFunc hcf = idle): healthFunc (hcf) {} int healthValue () const {return healthFunc (* this );}... private: HealthCalcFunc healthFunc;}; // different entities of the same character type can have different health calculations. Function, for example, class EvilBadGuy: public GameCharacter {public: explicit EvilBadGuy (HealthCalcFunc hcf = defaultHealthCalc): GameCharacter (hcf ){...}...}; int loseHealthQuickly (const GameCharacter &); // Healthy Function compute function 1int loseHealthSlowly (const GameCharacter &); // Healthy Function compute function 2 EvilBadGuy ebg1 (loseHealthQuickly ); // EvilBadGuy ebg2 (loseHealthSlowly) for people of the same type; // different health Calculation Methods // defualtHealthCalc does not access the non-public component of EvilBadGuy. // if you want to access Ask the non-public component to make it friends, so it is possible to // reduce the class encapsulation. // Use a function pointer to replace a virtual function. Its advantages are as follows: "Each object // has its own healthy computing function" and "changing computing functions at runtime ") whether it is enough to make up for the disadvantages (for example, it may reduce the encapsulation of GameCharacter //), you must choose based on the design situation.

<3>. The Strategy Mode is completed by the tr1: function.
// Consider the following code: class GameCharacter; int defaultHealthCalc (const GameCharacter &); class GameCharacter {public: // HealthCalcFunc can be any "callable entity ), // you can call and accept anything compatible with GameCharacter, and return anything // compatible with int. Details. Typedef std: tr1: function <int (const GameCharacter &)> HealthCalcFunc; explicit GameCharacter (HealthCalcFunc hcf = regular): healthFunc (hcf) {} int healthValue () const {return healthFunc (* this );}... private: HealthCalcFunc healthFunc;}; // Today's tr1: function is equivalent to a generalized pointer to a function, // It has more amazing elasticity in the case of "specifying healthy computing functions": short calcHealth (const GameCharacter &); // healthy computing function // note that the return type is non-intstruct healthCalculator {// function object int operator () (const GameCharacter &) designed for healthy computing &) const {...}}; class GameLevel {public: float health (const GameCharacter &) const; // member function, used to calculate health ;... // pay attention to its non-int return type}; class EvilBadGuy: public GameCharacter {// same as before ...}; class EyeCandyCharacter: public GameCharacter {// another character type ;... // assume its constructor and}; // EvilBadGuy is the same as EvilBadGuy ebg1 (calcHealth); // character 1, use a // function to calculate the health function EveCandyCharacter ecc1 (HealthCalculator ()); // character 2, use a certain // function object to calculate the health function GameLevel currentLevel ;... evilBadGuy ebg2 (// function 3, using a std: tr1: bind (& GameLevel: health, // member function compute health function currentLevel, _ 1 ,) // see the following for details); // use tr1: the benefit of function is that any callable object is allowed, // as long as its call form is compatible with the signature type

<4>. Classical Strategy Mode
// 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 function The possibility of adding an algorithm to the HealthCalcFunc inheritance system.
____________________________________________________________________________________________________________________________________
Cla36: Do not redefine the inherited non-virtual function
#1. Do not redefine the inherited non-virtual function
Reason (1): both non-virtual and redefined functions are statically bound,
The results of calling them are 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 calling the same object x, there are different behaviors. // This is not what we want, so we can redefine it, // It is better to make it a virtual function.
Reason (2). inherited functions are divided into virtual and non-virtual functions. virtual means
Its implementation is easy to replace, so dynamic binding is implemented, while non-virtual means its immutability.
Beyond specicity, it is applied to static binding because it inherits its implementation.
____________________________________________________________________________________________________________________________________
Clause 37: Do not redefine the inherited default parameter values
#1. Do not redefine the inherited default parameter values, because the default parameter values are static bindings,
The virtual function-the only thing you should override-is dynamic binding.
// A classclass Shape {public: enum ShapeColor {Red, Green, Blue} to describe the geometric Shape; // a function is required for all shapes, used to depict your own virtual void draw (ShapeColor color = Red) const = 0 ;...}; class Rectangle: public Shape {public: // Note: assign different default parameter values. Really bad! Virtual void draw (ShapeColor color = Green) const ;...}; class Circle: public Shape {public: virtual void draw (ShapeColor) const // note that when you write this code as an object to call this function, you must specify the parameter value. // This function does not inherit the default parameter value from its base under static binding. // However, if you call this function using a pointer (or reference), you do not need to specify its parameter value, // This function will inherit the default parameter value from its base after dynamic binding ...}; // consider the following pointer: Shape * ps; // The static type is Shape * pc = new Circle; // The static type is Shape * pr = new Rectangle; // The static type is Shape * pc-> draw (Shape: Red); // call Circle: draw (Shape: Red) pr-> draw (Shape :: red); // call Rectangle: draw (Shape: Red) // The virtual function of this Code is dynamically bound, the default parameter value is static binding //, which leads to a strange call method and cannot be called in a unified manner, the reason why the compiler does not dynamically bind the default parameter value is the runtime efficiency. // Can we specify the same default value for derived ?, Like this: class Rectangle: public Shape {public: virtual void draw (ShapeColor color = Red) const ;...}; // whether the answer is, the reason is very simple, which leads to repeated code, // and dependency. If the default value is changed one day, it will easily affect the whole body. // A better way is to let non-virtual specify the default value to replace work: class Shape {public: enum draw (ShapeColor color = Red) const // now it is a non-virtual {doDraw (color); // call a virtual }... private: virtual void doDraw (ShapeColor color) const = 0; // the real work is done here}; class Rectangle: public Shape {public :... private: virtual void doDraw (ShapeColor color) const; // you do not need to specify the default value ...}; // since the non-virtual function is never overwritten by the derived class, the default color parameter value of the // draw function is always true.
____________________________________________________________________________________________________________________________________
Clause 38: Use composite molding to produce has-a or "based on something"
#1. composite has double meanings. In application domain, composite means has-
(One ). In implementation domain
Is-implemented-in-terms-of (based on something ).
____________________________________________________________________________________________________________________________________
Cla39: wise and prudent use of private inheritance
#1. Private inheritance means is-implemented-terms-of (implemented based on something ). It
Usually lower than composition. However, when the derived class needs to access
The member of the protected base class, or the inherited virtual function needs to be redefined.
This design is reasonable.
// For example, if we want to use the functions in Timer, We Can implemented-in-terms-ofclass widget: private Timer {private: // private permission: Avoid customers from calling virtual void onTick () const; // redefine the onTick function functions we need ...};

#2. A design that replaces private inheritance is to use a composite class. Its usage is 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 (separation, dependent on declarative)

#3. The size of an independent non-affiliated object must not be zero, but as an affiliated object, it exists
EBO (Empty base optimization), that is, the optimization of the blank base class, can make it
The size is zero.
For example:
Class Empty {}; class HoldsAnInt: private Empty {private: int x;} // almost certainly, sizeof (HoldsAnInt) = sizeof (int)

#4. Unlike compresition, private inheritance can optimize empty base.
This may be important for library developers who are committed to minimizing object sizes.
# The Alternative Design of 2 can no longer be satisfied.
____________________________________________________________________________________________________________________________________
Clause 40: wise and prudent use of multi-Inheritance
#1. virtual inheritance will increase the cost of size, speed, and initialization, so do not use it unless necessary.
Virtual inheritance. It also means that the less data the virtual base class has, the more data it uses
The higher the value, we should avoid placing data in it as much as possible.

#2. When an inherited combination is involved, multiple inheritance takes full advantage of its legitimate use. For example, "public inherits
The combination of Interface class "and" private inheriting a class to be implemented:
Class IPerson {// This class indicates that the interface must be implemented public: virtual ~ IPerson (); virtual std: string name () const = 0; virtual std: string birthDate () const = 0; virtual std: string birthDate () const = 0 ;}; class DatebaseID {...}; // used later. 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): PersonInfo (pid) {} virtual std: string name () const // implement the necessary IPerson function {return PersonInfo: theName ();} virtual std: string birthDate () const // implement the necessary IPerson function {return PersonInfo :: theBirthDate ();} private: const char * valueDelimOpen () const {return ";} // redefine the inherited const char * valueDelimClose () const {return "";} // virtual "boundary function"};

____________________________________________________________________________________________________________________________________

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.