程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++的虛函數與抽象類

C++的虛函數與抽象類

編輯:關於C++

1.虛函數

1.1虛函數的作用

虛函數的作用是允許在派生類中重新定義與基 類同名的函數,並且可以通過基類指針或引用來訪問基類和派生類中的同名函數。

class Time{
 public:
  Time(int=0,int=0,int=0);
   void show();
 protected:
  int hour;
  int min;
   int sec;
};
class LocalTime:public Time{
 public:
   LocalTime(int=0,int=0,int=0,string="+8");
  void show();
  protected:
  string zone;
};
Time::Time(int h,int m,int s):hour (h),min(m),sec(s){}
void Time::show(){
  cout<<hour<<":"<<min<<":"<<sec& lt;<endl;
}
LocalTime::LocalTime(int h,int m,int s,string z):Time (h,m,s),zone(z){}
void LocalTime::show(){
  cout<<hour<<":"<<min<<":"<<sec& lt;<"@"<<zone<<endl;
}
int main(){
 Time t;
 LocalTime lt;
 Time *pt=&t;
 pt->show();
  pt=&lt;
 pt->show();
 system("PAUSE");
 return EXIT_SUCCESS;
}

結果:

0:0:0
0:0:0

這裡 通過指針找到派生類,但無法調用派生類show()。如果使用虛函數。

將基類Time中的 show()函數聲明為虛函數, 其余不變。

class Time{
 public:
  Time(int=0,int=0,int=0);
  virtual void show();

};

結果:

0:0:0

0:0:0@+8

本來,基類指針是指向基 類對象的,如果用它指向派生類對象,則進行指針類型轉換,將派生類對象的指針先轉換為 基類指針,所以基類指針指向的是派生類對象中的基類部分。在程序修改前,是無法通過基 類指針去調用派生類對象中的成員函數的。

虛函數突破這一限制,在派生類的基類部 分中,派生類的虛函數取代了基類原來的虛函數,因此在使用基類指針指向派生類對象後, 調用虛函數時就調用了派生類的虛函數。

1.2虛函數的使用方法

【1】在基類 用virtual聲明成員函數為虛函數。這樣就可以在派生類中重新定義此函數,為它賦予新的功 能,並能方便地被調用。

【2】在派生類中重新定義此函數,要求函數名、函數(返 回)類型、函數參數個數和類型與基函數的虛函數相同。如果在派生類中沒有對基類的虛函 數重定義,則派生類簡單地繼承直接基類的虛函數。

有一種情況例外,在這種情況下 派生類與基類的成員函數返回類型不同,但仍起到虛函數的作用。即基類虛函數返回一個基 類指針或基類引用,而子類的虛函數返回一個子類的指針或子類的引用。

class Base{
 public:
  virtual Base *fun(){
    cout<<"Base's fun()."<<endl;
   return this;
  }
};
class Derived:public Base{
 public:
  virtual Derived *fun(){
   cout<<"Derived's fun ()."<<endl;
   return this;
  }
};
void test (Base &x){
 Base *b;
 b=x.fun();
}
int main(){
  Base b;
 Derived d;
 test(b);
 test(d);
 system ("PAUSE");
 return EXIT_SUCCESS;
}

結果:

Base's fun().
Derived's fun().

【3】C++規 定,當一個成員函數被聲明為虛函數後,其派生類中的同名函數(符合2中定義的函數)都自 動成為虛函數。

【4】定義一個指向基類對象的指針變量,並使其指向同一類族中的 某個對象。通過該指針變量調用此函數,此時調用的就是指針變量指向的對象的同名函數。

1.3聲明虛函數的限制

【1】只能用virtual聲明類的成員函數,使它成為虛函 數,而不能將類外的普通函數聲明為虛函數。

【2】一個成員函數被聲明為虛函數後 ,在同一類族中的類就不能再定義一個非virtual的但與該虛函數具有相同參數(個數與類型 )和函數返回值類型的同名函數。

【3】靜態成員函數不能是虛函數,因為靜態成員 函數不受限於某個對象。

【4】inline函數不能是虛函數,因為inline函數是不能在 運行中動態確定其位置的。即使虛函數在類的內部定義,編譯時,仍將其視為非inline的。

【5】使用虛函數,系統要有一定的空間開銷。當一個類帶有虛函數時,編譯器會為 該類構造一個虛函數表(virtual function tanle,vtable),它是一個指針數組,存放每個虛 函數的入口地址。

2.虛析構函數

class Time{
 public:
  Time(int=0,int=0,int=0);
  ~Time(){
   cout<<"Time destructor"<<endl;
  }
 protected:
  int hour;
  int min;
  int sec;
};
class LocalTime:public Time{
 public:
  LocalTime(int=0,int=0,int=0,string="+8");
   ~LocalTime(){
   cout<<"LocalTime destructor"<<endl;
  }
 protected:
  string zone;
};
Time::Time(int h,int m,int s):hour(h),min(m),sec(s){}
LocalTime::LocalTime(int h,int m,int s,string z):Time(h,m,s),zone(z){}
int main(){
 Time *p=new LocalTime;//指向派生類
 delete p;
 system ("PAUSE");
 return EXIT_SUCCESS;
}

結果:

Time destructor

從結果可以看出,執行的還是基類的析構函數,而程序的本 意是希望執行派生類的析構函數。此時將基類的析構函數聲明為虛析構函數,

virtual ~Time(){
 cout<<"Time destructor"<<endl;
}

結果:

LocalTime destructor
Time destructor

如果將基類的析構函數聲明為虛函數,由 該基類所派生的所有派生類的析構函數也自動成為虛函數。

把基類的析構函數聲明為 虛函數的好處是,如果程序中delete一個對象,而delete運算符的操作對象是指向派生類對 象的基類指針,則系統會調用相應類的析構函數。

構造函數不能聲明為虛函數。

3.純虛函數

virtual void show()=0;//純虛函數

這裡將show()聲明 為純虛函數(pure virtual function)。純虛函數是在聲明虛函數時被“初始化” 為0的虛函數。

聲明純虛函數的一般形式為,

virtual 函數類型 函數名(參數 列表)=0;

純虛函數沒有函數體;最後的“=0”並不代表函數返回值為0, 它只起形式上的作用,告訴編譯器“這是純虛函數”;這個一個聲明語句,最後 有分號。

聲明純虛函數是告訴編譯器,“在這裡聲明了一個虛函數,留待派生 類中定義”。在派生類中對此函數提供了定義後,它才能具備函數的功能,可以被調用 。

純虛函數的作用是在基類中為其派生類保留了一個函數的名字,以便派生類根據需 要對它進行定義。

如果在一個類中聲明了純虛函數,而在其派生類中沒有對該函數定 義,則該函數在派生類中仍為純虛函數。

4.抽象類

將不用來定義對象而只作 為一種基本類型用作繼承的類,稱為抽象類(abstract class),由於它常用作基類,通常稱 為抽象基類。凡是包含純虛函數的類都是抽象類。

如果在派生類中沒有對所有的純虛 函數進行定義,則此派生類仍然是抽象類,不能用來定義對象。

可以定義指向抽象類 數據的指針變量。當派生類成為具體類後,就可以用這個指針指向派生類對象,然後通過該 指針調用虛函數。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved