【C++】階段性總結(三)——繼承和多態

繼承

類之間的關係

繼承

在已有類的基礎上創建新類的過程

一個B類繼承A類,或稱從類A派生B類——A稱爲基類(父類),B稱爲派生類(子類)

基類和派生類

class 派生類名 : 基類名錶
  {
        數據成員和成員函數聲明
  };

基類名錶 :

訪問控制  基類名1, 訪問控制  基類名2 ,… ,   訪問控制  基類名n

注意:==種方式繼承基類,派生類都不能直接使用基類的私有成員 ==

派生類的生成過程經歷了三個步驟:

在這裏插入圖片描述

派生類的對象結構

在這裏插入圖片描述

公有繼承

在這裏插入圖片描述
在這裏插入圖片描述
例題

定義一個基類person(不定義構造函數)
姓名、性別、年齡(訪問權限設置爲私有)
定義公有的成員函數set_p()
定義公有的成員函數display_p(),顯示person的信息
再由基類派生出學生類(不定義構造函數,採用公有繼承的方式)
增加學號、班級、專業和入學成績
定義公有成員函數set_t()
定義成員函定義公有的成員函數display_s(),顯示所有的信息
#include<iostream>
#include <string>
using namespace std;
class Person
{
 string name;
 int age;
 string sex;
public:
 void set_p() 
 {
  cout<<"name\tage\tsex"<<endl;
  cin>>name>>age>>sex;
 }
 void show_p() 
 {
   cout<<name<<"  "<<age<<"  "<<sex<<endl;
 }
};

方法一:

class student :public Person
{
 string no;
 string zhuanye;
 string t_class;
 float score;
public:
 void set_t()
 {
       set_p(); //調用繼承於基類的成員函數訪問繼承於基類的私有數據成員
     cout<<"zhuanye\tt_class\tscore"<<endl;
     //cin>>zhuanye>>t_class>>score;//錯誤,沒有輸入基類繼承過來的成員
     cin>>name>>age>>sex>>zhuanye>>t_class>>score;
     
 }
 void show_t() 
 {
  show_p();
  //cout<<zhuanye<<"  "<<t_class<<"  "<<score<<endl;//沒有顯示學號
  cout<<name<<"  "<<age<<"  "<<sex<<zhuanye<<"  "<<t_class<<"  "<<score<<endl;
 }
};

方法二:

class student :public Person
{
 string no;
 string zhuanye;
 string t_class;
 float score;       //設計自己成員
public:
        //改造基類成員 成員函數的覆蓋
 void set()
 {    //隱藏了基類中的同名成員       
      Person::set(); //調用繼承於基類的成員函數訪問繼承於基類的數據成員(首先調用基類函數)
         cout<<"zhuanye\tt_class\tscore"<<endl;
      cin>>zhuanye>>t_class>>score;
 }
 void show() 
 {
  Person::show();//不但顯示自己成員,還顯示基類成員  基類成員的使用
  cout<<zhuanye<<"  "<<t_class<<"  "<<score<<endl;
 }
};

重名成員

在這裏插入圖片描述

派生類中訪問靜態成員

在這裏插入圖片描述

基類初始化

在這裏插入圖片描述

例題
調用構造函數順序測試,構造函數無參數

#include<iostream>
using namespace std ;
class  Base
{ 
 public :  
  Base ( ) { cout << "Base created.\n" ;  }
} ;
class  D_class : public  Base
{ 
 //派生類構造函數
 public : 
   D_class ( ) {  cout << "D_class  created.\n" ;  }
} ;
int main ( )
{
 D_class d1 ;//先調用基類構造函數再本身 
}
派生類構造函數

==派生類構造函數和析構函數的定義規則 ==
派生類構造函數和析構函數的使用原則

  1. 類的構造函數和析構函數不能被繼承
  2. 如果基類沒有定義構造函數或有無參的構造函數, 派生類也可以不用定義構造函數
  3. 如果基類無無參的構造函數,派生類必須定義構造函數
  4. 如果派生類的基類也是派生類,則每個派生類只負責直接基類的構造
  5. 派生類是否定義析構函數與所屬的基類無關

在這裏插入圖片描述

調用基類構造函數對基類成員進行初始化。

在這裏插入圖片描述

      派生類::派生類名(參數總表):基類名(參數表)
     {
             // 派生類新增成員的初始化語句
     }
派生類析構函數

在這裏插入圖片描述

繼承的應用實例

在這裏插入圖片描述

class Point
{   friend ostream &operator<< (ostream &, const Point &);
  public:
    Point( int = 0, int = 0 ) ; // 帶默認參數的構造函數
    void setPoint( int, int ) ; // 對點座標數據賦值
    int getX() const { return x ; }     int getY() const { return y ; }
  protected:    int x, y; // Point類的數據成員
};
class Circle : public Point
{   friend ostream &operator<< (ostream &, const Circle &); // 友元函數
  public:
    Circle(double r=0.0, int x=0, int y=0); // 構造函數
    void setRadius(double);  /*置半徑*/          double getRadius() const;     /*返回半徑*/ 
    double area() const;  // 返回面積
  protected:    double radius; // 數據成員,半徑
};
class Cylinder:public Circle
{    friend ostream & operator<<(ostream &, const Cylinder &);    // 友元函數
   public:
     Cylinder(double h=0.0, double r=0.0, int x=0, int y=0);      // 構造函數
     void setHeight(double);    /* 置高度值*/           double getHeight() const;     /* 返回高度值*/
     double area() const;      /* 返回面積*/            double volume() const;     /* 返回體積*/
   protected:     double height; // 數據成員,高度
};

多繼承

在這裏插入圖片描述

class  派生類名 : 訪問控制  基類名1 ,  訪問控制  基類名2 ,, 訪問控制  基類名n
    {
         數據成員和成員函數聲明
    }

在這裏插入圖片描述

多繼承的構造函數
派生類名(參數總表):基類名1(參數表1),基類名2(參數表2),,基類名n(參數表n)
    {
           // 派生類新增成員的初始化語句
     } 

多繼承方式下構造函數的執行順序:

在這裏插入圖片描述

虛基類

  如果一個派生類從多個基類派生,而這些基類又有一個共同的基類,則在對該基類中聲明的名字進行訪問時,可能產生二義性。

賦值兼容原則
賦值兼容規則指在程序中需要使用基類對象的任何地方,都可以用公有派生類的對象來替代。

在這裏插入圖片描述

虛函數和多態

 多態性(Polymorphism)是指一個名字,多種語義;或界面相同,多種實現。

在這裏插入圖片描述

靜態聯編

聯編是指一個程序模塊、代碼之間互相關聯的過程。
靜態聯編,是程序的匹配、連接在編譯階段實現,也稱爲早期匹配。
重載函數使用靜態聯編

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

類指針的關係

基類指針和派生類指針與基類對象和派生類對象4種可能匹配:

 直接用基類指針引用基類對象;
 直接用派生類指針引用派生類對象;
 用基類指針引用一個派生類對象;
 用派生類指針引用一個基類對象。 

虛函數和動態編聯

冠以關鍵字 virtual 的成員函數稱爲虛函數

注意實現運行時多態的關鍵首先是要說明虛函數,另外,必須用
基類指針調用派生類的不同實現版本

基類指針雖然獲取派生類對象地址,卻只能訪問派生類從基類繼承的成員

#include<iostream>
using namespace std ;
class  Base
{ public :       Base(char xx)  { x = xx; }
                      void who()  { cout << "Base class: " << x << "\n" ; }
   protected:    char x;
} ;
class  First_d : public  Base
{ public :       First_d(char xx, char yy):Base(xx)  { y = yy; }
                      void who()  { cout << "First derived class: "<< x << ", " << y << "\n" ; }
   protected:    char y;
} ;
class  Second_d : public  First_d
{ public :
      Second_d( char xx, char yy, char zz ) : First_d( xx, yy ) { z = zz; } 
      void who()  { cout << "Second derived class: "<< x << ", " << y << ", " << z << "\n" ; }
   protected:    char z;
} ;
int main()
{ Base  B_obj( 'A' ) ;   First_d F_obj( 'T', 'O' ) ;  Second_d S_obj( 'E', 'N', 'D' ) ;
   Base  * p ;
   p = & B_obj ;    p -> who() ;
   p = &F_obj ;     p -> who() ;
   p = &S_obj ;     p -> who() ;
   F_obj.who() ;
   ( ( Second_d * ) p ) -> who() ;
}

運行結果:

在這裏插入圖片描述

在這裏插入圖片描述

注意
一個虛函數,在派生類層界面相同的重載函數都保持虛特性
虛函數必須是類的成員函數
不能將友元說明爲虛函數,但虛函數可以是另一個類的友元
析構函數可以是虛函數,但構造函數不能是虛函數

虛函數的重載
虛析構函數

例題

成員函數調用析構函數(採用動態聯編):

class Animal
{
 string name;
public:
 Animal(string a_name):name(a_name){}
 virtual void show(){}         //將來被派生類繼承
 void show_name()
 {
  cout<< "The name is "<<name<<".<<endl;
 }
};
class Cat :public Animal
{
 string kind;
public:
 Cat(string a_name,string a_kind):Animal(a_name),kind(a_kind)
 {}
 void show();          //覆蓋函數(同名同參數)
};
void Cat::show()
{
 show_name();
 cout<<" It's a "<<kind<<endl;
}
class Dog:public Animal
{
 string kind;
public:
 Dog(string a_name,string a_kind):Animal(a_name),kind(a_kind)
 {}
 void show();
};
void Dog::show()
{
 show_name();
 cout<<" It's a "<<kind<<endl;
}
class Tiger:public Cat
{
public:
 Tiger(string a_name,string a_kind):Cat(a_name,a_kind)
 {}
};
nt main()
{
 Animal *p;             //基類指針
 Cat cat("Tom","cat");
 Dog dog("Jerry","Dog");
 Tiger tiger("DuDu","Tiger");
 p=&cat;                //綁定貓對象
 p->show();
 p=&dog;  //綁定狗對象
 p->show();
 p=&tiger;  //綁定老虎對象
 p->show();
 return 0;
}

純虛函數抽象類

在這裏插入圖片描述注意
純虛函數在派生類中必須重寫

虛函數與多態的應用

在這裏插入圖片描述
例題在這裏插入圖片描述

class Employee
{ 
 public:
       Employee(const int,const string );
       virtual ~Employee();   
       const string getName() const;
       const int getNumber() const;
       virtual double earnings() const=0;//僱員工資純虛函數 
       virtual void print() const;
  protected:
       int number;  // 編號
       string name;  // 姓名
};
class Manager : public Employee
{ 
 public:
       Manager(const int , const string, double =0.0);
       ~Manager() { }
       void setMonthlySalary(double); //新增函數
       virtual double earnings() const;
       virtual void print() const;
  private:
      double monthlySalary ; 
};
class HourlyWorker : public Employee
{ 
 public:
       HourlyWorker(const long, const string, double=0.0, int =0 );
       ~HourlyWorker(){}
       void setWage(double);  
       void setHours(int);  
       virtual double earnings() const; 
       virtual void print() const; 
  private:
      double wage;
      double hours;
};
class PieceWorker : public Employee
{ 
 public:
       PieceWorker(const long , const string, double =0.0, int =0 );
       ~PieceWorker() { }
       void setWage ( double ) ; //完成件數 
       void setQuantity ( int ) ;  //價值
       virtual double earnings() const;
       virtual void print() const;
  private:
       double wagePerPiece; 
       int quantity;   
};

學習總結

通過對圖書館管理系統的一步步完善,漸漸把知識運用到了實際應用中,逐步消化慢慢掌握。
作業中用到的知識就是比着抄代碼,以後應該多寫代碼,熟能生巧。

C++結課了,曾經因爲C很喜歡編程,後來因爲什麼漸漸少了熱愛。
加下來面臨的是課程設計,是一個昇華的任務,要重拾熱愛,盡所能去完成任務!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章