effective C++ 学习(Inheritance and Object-Oriented Design)

Item 32: Make sure public inheritance models “is-a”

1.     Public inheritance means beingthe relationship of “is-a”.

2.     Ensure the functions in baseclasses are equal to be used in the derived. For example, the bird and penguin,rectangle and square etc. In order to address these problems, we can add the appropriateinterfaces in the base classes, as following:

class Bird

{

};

class FlyingBird: public Bird

{

virtual void fly();

};

class Penguin: public Bird

{

};

The following way might be not an appropriatemethod.

void error(const std::string& msg);//definedin other place

class Penguin:public Bird

{

public:

    virtual voidfly(){error("Attempt to make a penguinfly!");}

}

Note: we had better to advance time oferror happening. The code above will notice error at the running, but we hopeit be noticed at compile time.

Item 33: Avoid hiding inherited names.

1.     The name lookup order inderived classes is presented as: derived classesàbase classes à thenamespaces à global scope.

class Base

{

private:

int x;

public:

virtual void mf1()=0;

virtualvoid mf1(int);

virtual void mf2();

void mf3();

 void mf3(double);

};

class Derived: public Base

{

public:

virtual void mf1();

 voidmf3();

void mf4();

};

 

 

 

 

 

 

 

 


Derived d;

Int x;

d.mf1();

d.mf1(x); // error! Because of Derived::mf1 covers Base::mf1

d.mf2();

d.mf3();  

d.mf3(x); //error! Because of Derived::mf3 covers Base::mf3

Note: if some functions in the base classesare not derived in the derived classes when using public inheritance, it willbreak the principle of “is-a” in public inheritance.

1)     Of course, we can use “usingBase::mf1” to address the cover problem.

class Derived: public Base

{

public:

    using Base::mf1;

    virtual void mf1();

    void mf4();

};

2)     To address this problem byforwarding function.

class Derived: private Base

{

public:

  virtual void mf1()

  {Base::mf1();}

  void mf4();

};

Note: the private inheritance allow somefunctions in base classes may be not inherited, and call the base functions byforwarding function.

Item 34: Differentiate between inheritance of interfaceand inheritance of implementation.

1.     The purpose of declaring a purevirtual function is to let derived classes only inherit the interface.

2.     The purpose of declaring animpure virtual function is to let derived classes inherit its interface andimplementation. Next we discuss the following three implementation ways.

1)     Impure virtual function. if youwant to use the default functions in base classes, you don’t need to implementthem in its derived classes. However this way might bring errors.

2)     Combine pure virtual functionsand protected non-virtual functions.

3)     Provide definitions for thepure virtual functions.

 classAirport

{

public:

    Airport(const std::string& destination):m_destination(destination){}

    std::stringgetDestination()const{returnm_destination;}

private:

    std::stringm_destination;

};

class Airplane

{

public:

    virtual void fly(const Airport& destination);

};

voidAirplane::fly(const Airport& destination)

{

    std::cout<<"default model"<<destination.getDestination()<<std::endl;

}

class ModelA: public Airplane

{

};

class ModelB: public Airplane

{

};

class ModelC: public Airplane

{

};

int main()

{

    Airplane*pa= new ModelA;

    const Airport PDX("China,chengdu");

    pa->fly(PDX);

    pa = new ModelB;

    pa->fly(PDX);

    pa = new ModelC;

    pa->fly(PDX);

    return 0;

}


Note: ModelC should have carried out thedifferent fight mode. As result of not implementing “fly” function in thederived classes, the default “fly” function is called when using ModelC objectsto call “fly” function.

class Airport

{

public:

    Airport(const std::string&destination):m_destination(destination){}

    std::stringgetDestination()const{returnm_destination;}

private:

    std::stringm_destination;

};

class Airplane

{

public:

    virtual void fly(const Airport& destination)=0;

protected:

    void defaultfly(constAirport& destination);

};

voidAirplane::defaultfly(const Airport&destination)

{

    std::cout<<"default model"<<destination.getDestination()<<std::endl;

}

class ModelA: public Airplane

{

public:

    virtual void fly(const Airport& destination)

    {

       defaultfly(destination);

    }

};

class ModelB: public Airplane

{

public:

    virtual void fly(const Airport& destination)

    {

       defaultfly(destination);

    }

};

class ModelC: public Airplane

{

    virtual void fly(const Airport& destination)

    {

       std::cout<<"ModelC "<<destination.getDestination()<<std::endl;

    }

};

int main()

{

    Airplane*pa= new ModelA;

    const Airport PDX("China,chengdu");

    pa->fly(PDX);

    pa = new ModelB;

    pa->fly(PDX);

    pa = new ModelC;

    pa->fly(PDX);

    return 0;

}

 

Note: there are two advantages in thiscase. One is to depart interfaces and implementations; the other one is thatinterfaces and implementations can have different protection levels. Of course,it can’t be perfect. It will pollute namespace due to too much similar functionnames.

class Airplane

{

public:

    virtual void fly(const Airport& destination)=0;

};

voidAirplane::fly(const Airport& destination)

{

    std::cout<<"default model"<<destination.getDestination()<<std::endl;

}

class ModelA: public Airplane

{

public:

    virtual void fly(const Airport& destination)

    {

       Airplane::fly(destination);

    }

};

class ModelB: public Airplane

{

public:

    virtual void fly(const Airport& destination)

    {

       Airplane::fly(destination);

    }

};

class ModelC: public Airplane

{

public:

    virtual void fly(const Airport& destination)

    {

       std::cout<<"ModelC "<<destination.getDestination()<<std::endl;

    }

};



Note: pure virtual functions are alsodefined. Therefore we can provide its definitions in the abstract class. On theone hand, the derived classes must provide definitions of the pure virtualfunctions. On the other hand, users can select default implementations oruser-defined implementations according to requirements.

3.     A non-virtual function meansthat the derived classes will accept an interface and a mandatory (强制性的) implementation. Therefore it shouldn’t be redefined in the derivedclasses.

Item 35: Consider alternatives to virtual functions

1.     Call indirectly private virtualfunctions by a public non-virtual member function, which is named non-virtualinterface (NVI) that is a special express of Template Method (is different from C++ templates). We name thisnon-virtual function wrapper(外覆器) of virtual functions

class GameCharacter

{

public:

inthealthValue() const

{

           ...; //do somework beforehand

     int retVal= doHealthValue();

     ...; //do somework afterwards

     returnretVal;

}

...

private:

virtual int doHealthValue() const

{

     ...

}

};

Note: NVI’s advantages are reflected on the fact that thepublic non-virtual member functions can do some work before private virtualfunctions are called, also do some work after private virtual functions arecalled. For example, the non-virtual functions can lock a mutex, make runninglog entry, verify class restriction conditions, and verify some prerequisitesbeforehand and so on; meanwhile it can also do some work afterwards including unlockinga mutex, verify the conditions after calling private virtual functions, andverify class restriction conditions and so on.

classGameCharacter{

public:

    const inthealthValue() const

    {

       checkEnvironment();

       int retVal = doHealthValue();

       checkEnvironment();

       return retVal;

    }

private:

    virtual voidcheckEnvironment() const

    {

       std::cout<<"this is checkEnvironment of GameCharacter"<<std::endl;

    }

    virtual intdoHealthValue() const

    {

       int estimateHealthValue = 10;

       std::cout<<"this is GameCharacter"<<std::endl;

       return estimateHealthValue;

    }

};

class EvilBadGuy:public GameCharacter{

private:

    virtual voidcheckEnvironment() const

    {

       std::cout<<"this is checkEnvironment of EivlBadGuy"<<std::endl;

    }

    virtual intdoHealthValue() const

    {

       int estimateHealthValue = 100;

       std::cout<<"this is EvilBadGuy"<<std::endl;

       return estimateHealthValue;

    }

};

int main()

{

    EvilBadGuyevilbadguy;

    GameCharactergamecharacter;

    std::cout<<evilbadguy.healthValue()<<std::endl;

    std::cout<<gamecharacter.healthValue()<<std::endl;

 

    GameCharacter*pGameCharacter;

    pGameCharacter= new GameCharacter;

    std::cout<<pGameCharacter->healthValue()<<std::endl;

    pGameCharacter= new EvilBadGuy;

    std::cout<<pGameCharacter->healthValue()<<std::endl;

    return 0;

}



Note: what is concluded from the results isthat polymorphism also can be implemented by private virtual function inobjects or their pointers. The following picture shows the results that “doHealthValue()”is set as non-virtual function. As a consequence, the “doHealthValue()” inderived class will not be called.


2.     Implement strategy mode byfunction pointers (for example game design)

#include <iostream>

classGameCharacter;

intdefaultHealthCalc(const GameCharacter& gc);

 

classGameCharacter

{

public:

    typedef int(*HealthCalcFunc)(const GameCharacter&);

    explicit GameCharacter(HealthCalcFunc hcf =defaultHealthCalc)

       :healthFunc(hcf){}

    int healthValue()

    {

       return healthFunc(*this);

    }

    virtual void test() const

    {

       std::cout<<"this is GameCharacter"<<std::endl;

    }

private:

    HealthCalcFunchealthFunc;

};

intdefaultHealthCalc(const GameCharacter&gamecharacter)

{

    int defaultValue = 10;

    gamecharacter.test();

    std::cout<<"this is defaultHealthCalc"<<std::endl;

    return defaultValue;

}

 

class EvilBadGuy:public GameCharacter

{

public:

    explicit EvilBadGuy(HealthCalcFunc hcf =defaultHealthCalc)

       :GameCharacter(hcf){}

 

    virtual void test() const

    {

       std::cout<<"this is EvilBadGuy"<<std::endl;

    }

};

intloseHealthQuickly(const GameCharacter&game)

{

    game.test();

    std::cout<<"loseHealthQuickly"<<std::endl<<std::endl;

    return 100;

}

intloseHealthSlowly(const GameCharacter& game)

{

    game.test();

    std::cout<<"loseHealthSlowly"<<std::endl;

    return 1;

}

int main()

{

    EvilBadGuyebg1(loseHealthQuickly);

    EvilBadGuyebg2(loseHealthSlowly);

    std::cout<<ebg1.healthValue()<<std::endl;

    std::cout<<ebg2.healthValue()<<std::endl;

    return 0;

}

Note: the advantages of Strategy modeimplemented by function pointer are the following two points:

1)     We can get different healthfunctions for persons of the same type, which gives us more flexibility.

2)     The health calculationfunctions can be changed at run time, for example, we can provide a memberfunction “setHealthCalculator” to dynamically set the health calculation function.

Of course, this design mode also has problemswhen health calculation function needs to have access to private members ofclass. We can use friend functions or make the part used become public members,however, which weaken encapsulation (封装性) of class.

3.     Implement strategy bytr1::function

#include <iostream>

#include<functional>

classGameCharacter;

intdefaultHealthCalc(const GameCharacter& gc);

 

classGameCharacter

{

public:

    typedef std::tr1::function<int(const GameCharacter&)>HealthCalcFunc;

    explicit GameCharacter(HealthCalcFunc hcf =defaultHealthCalc)

       :healthFunc(hcf){}

    int healthValue() const

    {

       return healthFunc(*this);

    }

private:

    HealthCalcFunchealthFunc;

};

 

class EvilBadGuy:public GameCharacter

{

public:

    explicit EvilBadGuy(HealthCalcFunc hcf =defaultHealthCalc)

       :GameCharacter(hcf){}

};

short calcHealth(const GameCharacter& game)

{

    std::cout<<"this is calcHealth"<<std::endl;

    return 1;

}

structHealthCalculator

{

    int operator()(const GameCharacter& game) const

    {

       std::cout<<"this is function object HealthCalculator"<<std::endl;

       return 2;

    }

};

class GameLevel

{

public:

    float health(constGameCharacter& game) const

    {

       std::cout<<"this is health member function in GameLevel"<<std::endl;

       return 3;

    }

};

 

classEyeCandyCharacter:public GameCharacter

{

public:

    explicit EyeCandyCharacter(HealthCalcFunc hcf =defaultHealthCalc)

       :GameCharacter(hcf){}

};

int main()

{

    EyeCandyCharacterebg2(HealthCalculator::HealthCalculator());

    EvilBadGuyebg1(calcHealth);

   

    GameLevelcurrentLevel;

    EvilBadGuyebg3(std::tr1::bind(&GameLevel::health,currentLevel,std::tr1::placeholders::_1));

   

    std::cout<<ebg1.healthValue()<<std::endl;

    std::cout<<ebg2.healthValue()<<std::endl;

    std::cout<<ebg3.healthValue()<<std::endl;

   

    return 0;

}


Note: as to this design pattern, there arethe following several points worth noting:

1)     std::tr1::function<returntype (parameter type)> can save all callable entities (可调用物) which are compatible with target signature(目标签名式), for example “int” is similar to “short” etc.

2)     Member functions are differentfrom non-member functions, the former have a default parameter “this”, while thelatter don’t. Therefore we need to remove the default parameter bystd::tr1::bind().

3)     When we use function objects,if we directly use constructor function to create a temporary object, andregard it as a transmission parameter. There is ambiguity in thisimplementation. “HealthCalculator()” may donate call constructor withoutparameter, or donate call “operator ()”, which will cause object “ebg2” to notbe constructed. There are two solutions. One of them is to explicitly appointthe function you want to call. For example, “HealthCalculator::HealthCalculator()”.Other one is to declare an object independently. For example, HealthCalculatortemp;  EyeCandyCharacter ebg2(temp);

4.     Classical strategy patterns.


This is a combinatorial design idea. GameCharacter andHealthCalcFunc are the root classes of the two inheritance systems. In the way,system coupling will be reduced.

#include<iostream>

classGameCharacter;

 

classHealthCalcFunc

{

public:

    virtual ~HealthCalcFunc(){}

    virtual int calc(const GameCharacter& gc) const

    {

       std::cout<<"this HealthCalcFunc"<<std::endl;

       return 0;

    }

};

classSlowHealthLoser:public HealthCalcFunc

{

public:

    virtual int calc(const GameCharacter& gc) const

    {

       std::cout<<"this SlowhealthLoser"<<std::endl;

       return 1;

    }

};

classFastHealthLoser:public HealthCalcFunc

{

public:

    virtual int calc(const GameCharacter& gc) const

    {

       std::cout<<"this FastHealthLoser"<<std::endl;

       return 2;

    }

};

HealthCalcFunc defaultHealthCalc;

classGameCharacter

{

public:

    virtual ~GameCharacter(){deletepHealthCalc;}

    explicit GameCharacter(HealthCalcFunc* hcf =&defaultHealthCalc)

       :pHealthCalc(hcf){}

    int healthValue() const

    {

       return pHealthCalc->calc(*this);

    }

private:

    HealthCalcFunc*pHealthCalc;

};

class EvilBadGuy:public GameCharacter

{

public:

    explicit EvilBadGuy(HealthCalcFunc* hcf =&defaultHealthCalc)

       :GameCharacter(hcf){}

};

classEyeCandyCharacter:public GameCharacter

{

public:

    explicit EyeCandyCharacter(HealthCalcFunc* hcf =&defaultHealthCalc)

       :GameCharacter(hcf){}

};

int main()

{

    EvilBadGuyevilBadGuy(new SlowHealthLoser());

    std::cout<<evilBadGuy.healthValue()<<std::endl;

    EyeCandyCharactereyeCandyCharacter(new FastHealthLoser());

    std::cout<<eyeCandyCharacter.healthValue()<<std::endl;

    return 0;

}

 

Note: there are several points worth noting:

1)     The destructors of root classesshould be virtual functions.

2)     Implement polymorphism ofhealth calculate functions by adding new derived classes of HealthCalcFunc.

3)     Combining design patternsreduce coupling of system.

Item 36: Never redefine an inherited non-virtual function

1.     Never redefine an inheritednon-virtual function.

#include<iostream>

class B

{

public:

   voidmf(){std::cout<<"this is B"<<std::endl;}

};

class D:public B

{

public:

   voidmf(){std::cout<<"this is D"<<std::endl;}

};

int main()

{

   D temp;

   B* pB = &temp;

   D* pD = &temp;

   pB->mf();

   pD->mf();

   return 0;

}


Note: as to non-virtual function, theB::mf() and D::mf() are statically bounded, so non-virtual functions called bypB will be the ones defined by B forever. However, if they are virtualfunctions, there are different results because virtual functions aredynamically bounded.

Item 37: Never redefine a function’s inherited defaultparameter value.

1.     Firstly we can observe thefollowing codes:

std::stringstrColor[] = {"Red","Green","Blue"};

enum ShapeColor {Red, Green, Blue};

class Shape

{

public:

   virtual void draw(ShapeColor color = Red) const = 0;

};

class Rectangle:public Shape

{

public:

   virtual void draw(ShapeColor color = Green) const;

};

void Rectangle::draw(ShapeColor color ) const

{

   std::cout<<"thisis Rectangle, its color is "<<strColor[color]<<std::endl;

}

class Circle:public Shape

{

public:

   virtual void draw(ShapeColor color) const;

};

void Circle::draw(ShapeColor color) const

{

   std::cout<<"thisis Circle, its color is "<<strColor[color]<<std::endl;

}

int main()

{

   Shape *ps;

   Shape *pc = newCircle();

   Shape *pr = newRectangle();

   pc->draw();

   pr->draw();

   return 0;

}


Note: there are the following problemsworth noting:

1)     The results show the derivedclasses still use the default parameter in base class. Because virtualfunctions are dynamically bounded, but default parameters are staticallybounded. Static type of “pc” and “pr” is Shape*, meanwhile the defaultparameter is also static type, so “pc->draw()” and “pr->draw()” do calldefault parameter in base class.

2)     Static type of an object is thetype when the object is declared, while dynamic type is the current type. Forexample, pc’s static type is Shape*, and its dynamic type is Circle*. If we changepr’s static part into Rectangle*, we will get the results that we want, asfollowing:


But this case is not conforming to polymorphism principle.

2.     We can solve this problem byNVI (non-virtual interface), as following:

std::stringstrColor[] = {"Red","Green","Blue"};

enum ShapeColor {Red, Green, Blue};

class Shape

{

public:

   voiddraw(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;

};

void Rectangle::doDraw(ShapeColor color ) const

{

   std::cout<<"thisis Rectangle, its color is "<<strColor[color]<<std::endl;

}

class Circle:public Shape

{

private:

   virtual void doDraw(ShapeColor color) const;

};

void Circle::doDraw(ShapeColor color) const

{

   std::cout<<"thisis Circle, its color is "<<strColor[color]<<std::endl;

}

int main()

{

   Shape *ps;

   Shape *pc = newCircle();

   Rectangle *pr = newRectangle();

   pc->draw();

   pr->draw();

   return 0;

}


This case can separate static part and dynamic part, andlet non-virtual function deal with the static part and the virtual functiondeal with dynamic part, so that avoid producing unexpected mistakes.

Item 38: Model “has-a” or “is-implemented-in-terms-of”through composition.

1.     Composition (复合) is different from public inheritance. The former means “has-a” inthe application domain, and “is-implemented-in-terms-of” in the implementationdomain, while the latter is the relationship of “is-a”. There are explanationsabout “has-a” and “is-implemented-in-terms-of”.

About “has-a”:

class Person

{

public:

   PersonImpl(conststd::string& name,const Date& birthday,

       constAddress& addr);

   std::string name()const;

   std::string birthday() const;

   std::string address() const;

private:

   std::string theName;

   Date theBirthday;

   Address theAddress;

};

       Theperson has a name, has an address and has a date.

 About “is-implemented-in-terms-of”:

       template <typename T>

class Set

{

public:

    bool isMember(constT& item) const;

    void insert(constT& item);

    void remove(constT& item);

    std::size_tsize() const;

private:

    std::list<T>m_rep;

};

 

template <typename T>

inline bool Set<T>::isMember(constT& item) const

{//exist is ture,otherwise false

    return std::find(m_rep.begin(),m_rep.end(),item) !=m_rep.end();

}

template <typename T>

inline void Set<T>::insert(constT& item)

{

    if(!isMember(item))

       m_rep.push_back(item);

}

template <typename T>

inline void Set<T>::remove(constT& item)

{

    std::list<T>::iteratoriter = std::find(m_rep.begin(),m_rep.end(),item);

    if(iter!=m_rep.end())

       m_rep.erase(iter);

}

template<typename T>

inline std::size_tSet<T>::size() const

{

    return m_rep.size();

}

Note:

1)     A template class is admitted tobe compiled respectively, because template class will be compiled into binarycode only when it is instantiated, and it is impossible to be instantiated inthe independent implementation file.

2)     Obviously, the relationshipbetween list and set is not a “is-a”, because the data meeting list’srequirements is not conforming to set’s, so the “set” is not a special list,but they have common characters and we can combine these common points.Therefore this design pattern (composition) is designed.

Item 39: Use private inheritance judiciously.

1.     Firstly private inheritancedoesn’t meet the relationship of “is-a”.

class Person

{

public:

  virtual void eat()const{std::cout<<"Person eat"<<std::endl;}

};

class Student:private Person

{

public:

  virtual void eat()const{std::cout<<"Student eat"<<std::endl;}

};

void eat(const Person&person)

{

  person.eat();

}

int main()

{

  Person p;

  Student s;

  eat(p);

  eat(s);//error!

  return 0;

}

Note: as to private inheritance, compilerdoesn’t convert the derived classes into base classes automatically.

2.     Private inheritance onlyinherits implementation of base classes, so it has significances atimplementation but not at designation.

3.     We may choose privateinheritance under the following two cases:

1)     When we need to use protectedfunctions and overload virtual functions in the class.

2)     When the EBO (empty base optimization) is taken intoconsideration. In order to reduce size of the derived classes. But this is anextreme case.

4.     Considering the case thatvirtual functions in the classes need to be overloaded, public inheritanceadding composition will be a good alternate design. Let’s consider thefollowing code.

class Timer

{

public:

Timer(){}

explicit Timer(int tickFrequency){}

virtual void onTick() const

{

     std::cout<<"Timer"<<std::endl;

}

};

class Widget:private Timer

{

private:

virtual void onTick() const

{

     std::cout<<"Widget"<<std::endl;

}

};

class Widget

{

private:

classWidgetTimer: public Timer

{

public:

     virtual void onTick() const

     {

        std::cout<<"WidgetTimer"<<std::endl;

     }

};

WidgetTimer timer;

};

Note: the case of public inheritance +composition has two advantages:

1)     It can stop derived classesredefine the virtual functions;

2)     It can reduce compilingdependence by defining the pointer of widgetTimer.

Item 40: Use multiple inheritance judiciously

1.     Multiple inheritance (MI) andsingle inheritance (SI):

Some questions about MI are shown as following:

#include<iostream>

classBorrowableItem

{

public:

    void checkOut();

};

classElectronicGadget

{

private:

    bool checkOut() const;

};

class MP3Player:

    public BorrowableItem ,

    public ElectronicGadget

{

};

int main()

{

    MP3Playermp;

    mp.checkOut();//error!

    //modified

    mp.BorrowableItem::checkOut();

    return 0;

}

Note: C++ firstly finds the best match andthen checks the functions of access authority. If you want to normally use the “checkout()”of the base class, you need to appointer out their bases.

2.     Diamond type multipleinheritance (钻石型多重继承)

 

#include<iostream>

classFile                                            

{

public:

    int m_test;                                                              

};

class InputFile:public File

{

};

class OutputFile:public File

{

};

class IOFile:public InputFile,

    public OutputFile

{

};

int main()

{

    IOFileiofile;

    iofile.m_test;    //IOFile::m_testis ambiguous

    iofile.InputFile::m_test;  //OK

    iofile.OutputFile::m_test;//OK

    return 0;

}

Note: under this case, the “m_test” in thebase class File is copied twice, in other words, there are two “m_test”s in theIOFile, one of them comes from InputFile and the other one comes fromOutputFile. If you only want to need one, you should use virtual inheritance.

3.     However, there are thefollowing problems about virtual inheritance:

1)     The derived classes obtained byvirtual inheritance will be larger than the ones by non-virtual inheritance.

2)     In the virtual inheritancesystem, the most derived class need to initialize virtual base classes. Thisfact implies two points. One of them is that the most derived class must knowits virtual bass. The other one is that when new derived class is added, itmust initialize its virtual classes.

4.     The advises about virtualinheritance:

1)     Try to avoid using virtualinheritance;

2)     If you must use virtualinheritance, you shouldn’t put data in the base. E.g. abstract class.

5.     The case exists, which thederived class needs to use the interfaces of one class and the implementationsof other class, as the following code.

class DatabaseID;

class PersonInfo

{

public:

    explicit PersonInfo(constDatabaseID& id){}

    virtual ~PersonInfo(){}

    virtual const char* theName() const;

    virtual const char* theBirthday() const;

private:

    virtual const char* valueDelimOpen() const;

    virtual const char* valueDelimClose() const;

};

const char* PersonInfo::theName()const

{

    static charvalue[100];

    std::strcpy(value,valueDelimOpen());

    std::strcat(value,"test");

    std::strcat(value,valueDelimClose());

    return value;

}

const char* PersonInfo::theBirthday()const

{

    static charvalue[100];

    std::strcpy(value,valueDelimOpen());

    std::strcat(value,"2017//10//12");

    std::strcat(value,valueDelimClose());

    return value;

}

const char* PersonInfo::valueDelimOpen() const

{

    return "[";

}

const char* PersonInfo::valueDelimClose()const

{

    return "]";

}

class IPerson

{

public:

    virtual ~IPerson(){}

    virtual std::string name() const =0;

    virtual std::string birthDate() const = 0;

};

class CPerson: public IPerson,

    private PersonInfo

{

public:

    explicit CPerson(constDatabaseID& pid):PersonInfo(pid){}

    virtual std::string name() const

    {

       return PersonInfo::theName();

    }

    virtual std::string birthDate() const

    {

       return PersonInfo::theBirthday();

    }

private:

    const char*valueDelimOpen() const {return "";}

    const char* valueDelimClose()const {return "";}

};

class DatabaseID{public:DatabaseID(){}};

 

int main()

{

    IPerson*person =new CPerson(DatabaseID::DatabaseID());

    std::cout<<person->name()<<std::endl;

    std::cout<<person->birthDate()<<std::endl;

    return 0;

}

Note: private inheritanceinherits function implementations of PersonInfo, and public inheritanceinherits function interfaces.
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值