做者:LogMsegmentfault
本文原載於 https://segmentfault.com/u/logm/articles,不容許轉載~函數
class Person {...}; class Student : public Person {...}; void eat(const Person& p); void study(const Student& s); eat(p); // ok eat(s); // ok,Student能夠視做Person調用函數 study(s); //ok study(p); //error,Person不能視做Student
int x; //global變量 void someFunc() { double x; //local變量 std::cin >> x; //local變量的x遮掩了global變量的x,實際起做用的是local變量的x }
//定義基類 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(); void mf3(); void mf4(); ... }; //使用 Derived d; int x; ... d.mf1(); //ok,調用Derived::mf1 d.mf1(x); //bad,由於Derived::mf1遮掩了Base::mf1 d.mf2(); //ok,調用Base::mf2 d.mf3(); //ok,調用Derived::mf3 d.mf3(x); //bad,由於Derived::mf3遮掩了Base::mf3
//解決方法1 //定義派生類 class Derived : public Base { public: using Base::mf1; //讓基類中名爲mf1和mf3的全部東西在此做用域內可見 using Base::mf3; virtual void mf1(); void mf3(); void mf4(); ... }; //使用 Derived d; int x; ... d.mf1(); //ok,調用Derived::mf1 d.mf1(x); //ok,調用Base::mf1 d.mf2(); //ok,調用Base::mf2 d.mf3(); //ok,調用Derived::mf3 d.mf3(x); //ok,調用Base::mf3
//解決方法2 //定義基類 class Base { public: virtual void mf1() = 0; virtual void mf1(int); ... }; //定義派生類 class Derived : public Base { public: virtual void mf1() { Base::mf1(); } // 轉交函數 ... }; //使用 Derived d; int x; ... d.mf1(); //ok,調用Derived::mf1 d.mf1(x); //bad,由於Base::mf1被遮掩,且Base::mf1(int)沒有被轉交
以下代碼所示,有3類繼承關係:this
class Shape { public: virtual void draw() const = 0; virtual void error(const std::string& msg); int objectID() const; ... }; class Rectangle : public Shape {...}; class Ellipse : public Shape {...};
//假設你正在寫一個遊戲軟件,每一個遊戲人物都應該有"健康"這個屬性 class GameCharacter { public: virtual int healthValue() const; //返回遊戲人物的健康狀態 ... };
上面的寫法沒有問題,基類提供了接口和一個缺省的實現,派生類能夠選擇是否重寫這個函數。可是,做者也提醒,還有一些其它的寫法:設計
//方法1:藉由Non-Virtual Interface(NVI)手法實現Template Method模式 class GameCharacter { public: int healthValue() const { ... //在開始主函數前,能夠作一些額外的工做 int retVal = doHealthValue(); //這個函數能夠被子函數重寫 ... //在結束主函數後,能夠作一些額外的工做 return retVal; } ... private: virtual int doHealthValue() const { ... } };
//方法2:藉由函數指針實現Strategy模式 //這種方法容許同一個派生類的不一樣對象使用不一樣的函數計算健康狀態 class GameCharacter { public: typedef int (*HealthCalcFunc)(const GameCharacter&); explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf) { } int healthValue const { return healthFunc(*this); } ... private: HealthCalcFunc healthFunc; };
//方法3:藉由tr1::function完成Strategy模式 //這種方法比函數指針更自由,它能夠是函數指針,也能夠是任何能夠被調用的東西 class GameCharacter { public: typedef std::tr1::function<int (const GameCharacter&)> HealthCalcFunc; explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc) : healthFunc(hcf) { } int healthValue const { return healthFunc(*this); } ... private: HealthCalcFunc healthFunc; }
//方法4:古典的Strategy模式 //HealthCalcFunc來自另外一個繼承體系 class GameCharacter; class HealthCalcFunc { public: ... virtual int calc(const GameCharacter& gc) const {...} ... }; HealthCalcFunc defaultHealthCalc; class GameCharacter { public: explicit GameCharacter(HealthCalcFun* phcf = &defaultHealthCalc) : pHealthCalc(phcf) { } int healthValue() const { return pHealthCalc->calc(*this); } ... private: HealthCalcFunc* pHealthCalc; };
//基類 class B { public: void mf(); ... }; //派生類 class D { public: void mf(); //遮掩B::mf ... }; //使用 D x; B* pB = &d; //調用B::mf() pB->mf(); D* pD = &d; //調用D::mf() pD->mf(); //若是B::mf()是virtual函數,則上面兩處都是調用D::mf() //因此也就能夠解釋,條款7所述的"使用多態時,基類的析構必須爲virtual"
//基類 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; ... }; //使用 Shape* pR = new Rectangle; //請注意類型是Shape* pR->draw(); //調用Rectangle::draw(Shape::Red),由於缺省值是在編譯期間靜態綁定的,而pR的靜態類型爲Shape*,是基類
//基類 class Shape { public: enum ShapeColor {Red, Green, Blue}; void draw(ShapeColor color=Red) const { doDraw(color); } ... private: virtual void doDraw(ShapeColor color) const = 0; }; //派生類 class Rectangle : public Shape { public: ... private: virtual void doDraw(ShapeColor color) const { ... } ... };
class Address {...}; class PhoneNumber {...}; class Person { public: ... private: std::string name; Address address; PhoneNumber voiceNumber; PhoneNumber faxNumber; };
class Person {...}; class Student : private Person {...}; void eat(const Person& p); Person p; Student s; eat(p); //ok eat(s); //bad,Student不能被視爲Person