我的隨行筆記11 C++ Primer Plus

qwer1030274531發表於2020-09-02

1.派生類不能直接訪問基類的私有成員,必須透過基類方法進行。 派生類可以直接訪問(呼叫)基類的公有類方法。


   創造派生類物件時,程式先建立基類物件.  派生類建構函式必須使用基類建構函式。 


class TablePlayer{ //基類宣告,在.h檔案中

private: 

  string  firstname;

  string lastname;

public:

  TablePlayer(const string & fn="none", const string & ln="none");

  void Name() const;

};

//方法的實現在.cpp檔案中

TablePlayer::TablePlayer(const string & fn, const string & ln) : firstname(fn), 

                         lastname(ln) {}   //成員初始化列表法

void TablePlayer::Name() const

{ std::cout<<lastname<<","<<firstname }   

 

class ReatedPlayer: public TablePlayer{ //公有派生類

private:

  unsigned int rating;

public:

  ReatedPlayer(unsigned int r, const string & fn="none",

               const string & ln="none") 

  ReatedPlayer(unsigned int r,const TablePlayer & tp); //

  unsigned int Rating() const {return rating; }

};

//派生類的建構函式

ReatedPlayer::ReatedPlayer(unsigned int r, const string & fn,

        const string & ln) : TablePlayer(fn,ln) //將fn,ln引數從派生類建構函式傳遞到基類建構函式

{

  rating=r;

}

 

ReatedPlayer::ReatedPlayer(unsigned int r, const string & fn,

        const string & ln) //未呼叫基類建構函式時,等效於使用預設建構函式  : TablePlayer() 

{

  rating=r;

}

 

 ReatedPlayer::ReatedPlayer(unsigned int r,const TablePlayer & tp):TablePlayer(tp)

  rating=r;

}

 ReatedPlayer::ReatedPlayer(unsigned int r,const TablePlayer & tp):

     TablePlayer(tp),rating(r) {} 

int main (void){

 using std::cout; using std::endl;

 TablePlayer player1("Bob", "Boomede");

 RatedPlayer rplayer1(1140, "Mallay","Duck"); //建立派生類物件

 rplayer1.Name(); //派生類可以直接呼叫基類的公有類方法

}

2.派生類的物件和地址可以賦給基類引用和指標。 但不能將基類的物件和地址賦給派生類引用和指標


 TablePlayer  & rt=rplayer;   TablePlayer  * pt= & rplayer; (合法)


3. C++的3種繼承方式:公有繼承 ,保護繼承 和 私有繼承 。公有繼承(is-a 關係 即派生類物件也是一個基類物件)


  繼承可以在基類基礎上新增屬性,但不能刪除基類屬性。


4. 同一個方法在派生類和基類中的行為不同,即 同一方法的行為隨上下文而異,這種行為叫多型。


   實現多型公有繼承的兩種方法:1) 在派生類中重新定義基類  2)使用虛方法(宣告函式時在前面加關鍵字virtual)具體實現不用加。

 在基類中將派生類會重新定義的方法宣告為虛方法. (程式將根據物件型別而不是引用或指標型別來選擇方法版本)

方法在基類中被宣告為虛擬函式後,派生類中將自動成為虛擬函式(也可加virtual指明哪些是虛擬函式)。

為基類宣告一個虛解構函式也是一種慣例。【確保釋放派生類物件時,按正確的順序呼叫解構函式。 先呼叫派生類解構函式,再呼叫基類建構函式】 分析:delete pt ; 釋放記憶體時,先應用派生類預設的解構函式,釋放派生類物件記憶體,再呼叫基類中的虛解構函式來釋放記憶體。 如果不在基類中宣告虛解構函式,直接呼叫基類的解構函式,將只釋放部分記憶體,不會釋放派生類中新的類成員指向的記憶體。

/*基類Brass公有成員函式ViewAcct宣告前無virtual*/

 Brass dom(" Dominic Banker",1121,4182.45);//基類物件

 BrassPlus dot("Doroty Banker",1238,2592.00);//派生類物件

 Brass & b1=dom; 

 Brass & b2=dot; //派生類可以直接賦給基類

 b1.ViewAcct(); // 用Brass::ViewAcct() 

 b2.ViewAcct(); // 用Brass::ViewAcct()

/*基類Brass公有成員函式ViewAcct宣告前有virtual*/

 b1.ViewAcct(); // 用Brass::ViewAcct() 

 b2.ViewAcct(); // 用BrassPlus::ViewAcct() 根據物件型別而非引用選擇版本

5. 在派生類的成員函式實現中:呼叫基類公有函式時,可直接呼叫如 doubal bal=Balance(),若派生類中虛擬函式ViewAcct()中要呼叫基類的虛擬函式ViewAcct(),要用作用域解析符,應寫為 Brass::ViewAcc()


6. 可以使用一個陣列來表示多種型別的物件,這就是多型性。


  Brass * p_client[N]; //Brass[i]指標可以指向Brass物件也可指向BrassPlaus物件


 p_client[i]=new Brass(temp,tempnum,tempbal); 


 p_client[j]=new BrassPlaus(temp,tempnum,tempbal,tmax,trate); //滿足不同的條件用相應的賦值語句


多型性由以下程式碼提供:


for (int i=0;i<N;i++){   p_client[i]->ViewAcct();   }


 


7.在編譯過程中進行聯編, 稱為靜態聯編 ; (可以直接確定編譯哪個函式)


   編譯器必須生成能夠在程式執行時選擇正確的虛擬函式程式碼,稱為動態聯編。


8. 動態聯編可以重新定義類方法, 靜態聯編效率高。


9.編譯器處理虛擬函式的方法:


   給每個物件新增一個隱藏成員。這個成員儲存了一個指向函式地址陣列的指標,這種陣列稱為虛擬函式表(vtbl:virtual function table,)。虛擬函式表中儲存了為類物件宣告的虛擬函式的地址。 虛擬函式地址表時一個陣列。

【基類物件中隱含一個指標,該指標指向基類中所有虛擬函式的地址表。派生類物件將隱含一個指向獨立地址表的指標。          如果派生類提供了虛擬函式的新定義,該虛擬函式表將儲存為新的地址,如果派生類沒有重新定義虛擬函式,該vtbl將儲存基類函式的原始地址】

(無論類中的虛擬函式是1個還是10個,只在物件中新增1個地址成員,只是表的大小不同)

10. 友元不能是虛擬函式 (友元不是類成員,只有成員才能是虛擬函式)


11.重新定義的繼承方法並不是過載。


   重新定義的繼承方法,應確保與基類的原型完全相同。但如果有返回型別,返回是基類引用或指標,則在派生類繼承方法修改為返回指向派生類的引用或指標。                                                                                                                                         class Dwelling {  public: virtual Dwelling & build (int n)  } ;                                                                                                         class Hovel:public Dwelling {  public: virtual Hovel & build (int n) };

如果基類宣告被過載,則應在派生類中重新定義所有的過載版本。                                                                                            class Dwelling {  public: virtual void  showperks ( ) const ;   virtual void  showperks (int a ) const ;  } ;                                    class Hovel:public Dwelling {  public: virtual void  showperks ( ) const ;   virtual void  showperks (int a ) const ;  }; 

12.  派生類成員可以直接訪問基類的保護成員,但不能直接訪問基類的私有成員。


       對外部世界來說,保護成員和私有成員類似;對派生類來說,保護成員和公有成員類似;

       對類資料成員最好採用私有訪問控制,不要使用保護訪問控制;同時透過基類的方法使派生類訪問基類資料;

        對於成員函式,保護控制有用,讓派生類訪問公眾不能使用的內部函式

13. 複雜抽象基類(ABC:abstract base class) :只定義介面,不定義實現。至少使用一個純虛擬函式介面,從ABC派生的類將根據派生類的具體特徵,使用常規虛擬函式來實現這種介面。 要成為真正的ABC,必須至少包含一個純虛擬函式。 原型中的=0使虛擬函式成為純虛擬函式。


【當N類和M類有一定的共性,但M類不需要完全繼承N類的資料和方法時,可以將M和N的共性放到ABC中,然後從ABC中派生出N類和M類。 ABC類指標可以同時管理M類和N類物件】 原型使用=0指出類是一個抽象基類。 C++中允許純虛擬函式有定義。


例如: 橢圓和圓兩個類, 共同點是中心座標方法,和移動Move()方法是相同的,但面積Area()是不同的。


           在ABC中不能實現Area()方法。 C++透過用純虛擬函式提高未實現的函式,純虛擬函式宣告的結尾處為=0;


            當類宣告中包含純虛擬函式時,不應該建立該類的物件。  


//橢圓和圓的ABC類  

class BaseEllipse{   //宣告中含有純虛擬函式,不能建立BaseEllipse類物件

  private: 

     double x; //橢圓和圓的共性

     double y;

  public:

     BaesEllipse(double x0=0,double y0=0) :x(x0),y(y0){}

     virtual ~BaesEllipse();

     void Move(int nx,ny) { x=nx; y=ny; } //橢圓和圓的共性

     virtual double Area() const=0; //純虛擬函式  橢圓和圓的不同     

}; 

14. 建構函式、解構函式、賦值函式、友元函式不能被繼承。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30239065/viewspace-2716589/,如需轉載,請註明出處,否則將追究法律責任。

相關文章