C++繼承中虛函數的使用


一:繼承中的指針問題。
1. 指向基類的指針可以指向派生類對象,當基類指針指向派生類對象時,這種指針只能訪問派生對象從基類繼承

而來的那些成員,不能訪問子類特有的元素 ,除非應用強類型轉換,例如有基類B和從B派生的子類D,則B

*p;D  dd; p=ⅆ是可以的,指針p只能訪問從基類派生而來的成員,不能訪問派生類D特有的成員.因為基類不

知道派生類中的這些成員。
2. 不能使派生類指針指向基類對象 .
3. 如果派生類中覆蓋了基類中的成員變量或函數,則當聲明一個基類指針指向派生類對象時,這個基類指針只能

訪問基類中的成員變量或函數。例如:基類B和派生類D都定義了函數f,則B *p; D m; p=&m; m.f()將調用基類中

的函數f()而不會調用派生類中的函數f()。
4. 如果基類指針指向派生類對象,則當對其進行增減運算時,它將指向它所認為的基類的下一個對象,而不會指

向派生類的下一個對象,因此,應該認為對這種指針進行的增減操作是無效的.
二:虛函數
1. 為什么要使用虛函數:正如上面第1 和3 點所講的,當聲明一個基類指針指向派生類對象時,這個基類指針只

能訪問基類中的成員函數,不能訪問派生類中 特有的成員變量或函數 。如果使用虛函數就能使這個指向派生類對

象的基類指針訪問派生類中的成員函數,而不是基類中的成員函數,基於這一點派生類中的這個成員函數就必須和

基類中的虛函數的形式完全相同,不然基類指針就找不到派生類中的這個成員函數。使用虛函數就實現了一個接口

多種方法。
2. 注意不能把成員變量聲明為虛有的,也就是說virtual關見字不能用在成員變量前面。
3. 正如上面所介紹的,一般應使用基類指針來調用虛函數,如果用點運算符來調用虛函數就失去了它的意義.
4. 如果基類含有虛函數則當聲明了一個基類的指針時 ,當基類指針指向不同的派生類時,它就會調用相應派生

類中定義的虛函數版本.這種調用方法是在運行時 決定的 ,例如在類B中聲明了虛函數,C,D,E 都從B繼承而

來且都實現了自已的虛函數版本,那么當定義了一個B類的指針P時,當P指向子類C時就會調用子類C中定義的

虛函數,當
P指向子類D時就會調用子類D中定義的虛函數 ,當P指向子類E時就會調用子類E中定義的虛函數 .
5. 虛函數須在基類中用virtual 關見字聲明也可以在基類中定義虛函數,並在一個或多個 子類中重新定義 .重

定義虛函數時不需再使用virtual關見字,當然也可以繼續標明virtual關見字,以便程序更好理解。
6. 包括虛函數的類被稱為多態類.C++使用虛函數支持多態性.
7. 在子類中重定義 虛函數時 ,虛函數必須有與基類虛函數的聲明完全相同的參數類型和數量,這和重載是不同

的.如果不相同,則是函數重載,就失去了虛函數的本質.
8. 虛函數不能是聲明它的類的友元函數,必須是聲明它的類的成員函數,不過虛函數可以是另一個類的友元.
9. 一旦將函數聲明為虛函數,則不管它通過多少層繼承,它都是虛函數,例如D和B繼承,而E又從D繼承,那

么在B中聲明的虛函數,在類E中仍然是虛函數.
10.隱藏虛函數:如果基類定義了一個 虛函數 ,但派生類中卻定義了一個虛函數的重載板本 ,則派生類的這個

版本就會把基類的虛函數隱藏掉,當使用基類指針調用該函數時只能調用基類的虛函數 ,而不能調用派生類的重

載版本,當用派生類的對象調用基類的 虛函數時就會出現錯誤了 ,因為基類的虛函數被派生類的重載版本隱藏了


11.帶默認形參的虛函數:當基類的虛函數帶有默認形參時,則派生類中對基類 虛函數的重定義也必須有相同數

量的形參,但形參可以有默認值也可以沒有,如果派生類中的 形參數量和基類中的不一樣多 ,則是對基類的虛函

數的重載 。
對虛函數的重定義也就意味着,當用指向派生類的基類指針調用該虛函數時就會調用基類中的虛函數版本。比如基
類定義virtual void f(int i=1, int j=2){}則派生類中必須定義帶有兩個形參的函數f才是對基類虛函數f的重定

義, 不然就是函數f的重載版本,比如派生類中定義的void f(),void f(int i),void f(int i=2)都是對函數f

的重載,不是對f的重定義。而void f(int i, int j),void f( int i, int j=3),void f(int i=4, int j=5)都

是對虛函數f的重定義。
12.如果虛函數形參有默認值,那么派生類中的虛數的形參不論有無默認值,當用指針調用派生類中的虛函數時就

會被基類的默認值覆蓋,即派生類的默認值不起作用 。但用派生類的對象調用該函數時,就不會出現這種情況 。
13.當用指向派生類的基類指針調用虛函數時是以基類中的虛函數的形參為標准的,也就是只要調用的形式符合基

類中定義的虛函數的標准就行了。比如基類中定義virtual void f(int i=1,int j=2){}派生類中重定義為void f

(int i, int j=3){}這時如果用派生類的對象調用這個派生類中的 虛函數 f 時必須至少要有一個實參,但是用指

向派生類的基類指針調用該虛函數時就可以不用任何形參就能調用派生類中的這個函數f, 比如語句p->f()就會調

用派生類中的 虛函數版本 。當用指向派生類的基類指針調用虛函數時是以基類中的虛函數的形參為標准的,也就

是只要調用的 形式符合基類中定義的虛函數的標准就行了。
14.析構函數可以是虛函數,但構造函數不能.
15.純虛函數聲明形式為 virtual 類型 函數名(參數列表)=0;注意后面的等於0;
16.如果類至少有一個純虛函數,則這個類就是抽象的。
17.如果基類只是聲明虛函數而不定義虛函數則此虛函數是純虛函數 . 任何派生類都必須實現純虛函數的自已的

版本. 如果不實現純虛函數那么該類也是抽象類。
18.抽象類不能有對象,抽象類只能用作其它類的基類,因為抽象類中的一個或多個函數沒有定義,所以不能用抽

象類聲明對象,
19.仍然可以用抽象類聲明一個指針,這個指針指向派生類對象.
20.如果派生類中未定義虛函數 ,則會使用基類中定義的函數.
21.虛函數虛擬特性是以層次結構的方式來繼承的,例如C從B派生而且C中重定義了B中的虛函數,而D又從C

派生且未重定義B中的虛函數,這時聲明一個基類指針P,當P指向類D,並調用D中的虛函數時,由於D中未重

定義虛函數他會調用基類中的虛函數版本,這時他會調用類C中的虛函數而不是類B中的虛函數,因為類C比類B

更接近於類D.
#include <iostream>

using namespace std;
class A
{
    public:
    int b;  
        //virtual int b;  //錯誤,不能把成員變量聲明為虛有的
    virtual void f()
    {
        cout<<"繼續"<<endl;
    }
    virtual void h(int i=1,int j=2)
    {
        cout<<"繼續H"<<endl;
    }
    ~A(){
        cout<<"析構A"<<endl;
    }
};
class B:public A
{
    public:
    int b;
    void f(int i)//重載虛函數f
    {
        cout<<"paif()"<<endl;
    }
    void f(){//在派生類中重定義虛函數f
        cout<<"paixu"<<endl;
    }
    void h(){ //重載虛函數h的版本。注意這里不是對基類虛函數的重定義。
        int b;
        b=5;
        cout<<"B的"<<b<<endl;
    }
    void h(int i,int j=3){
        int b;
        b=j;
        cout<<"paixuH"<<b<<endl;
    }//當基類中的虛函數有默認形參時,派生類中重定義基類中的虛函數的版本必須有相同數量的形參,
     //形參可以有默認值,也可以沒有。如果形參數量不一樣多則是對虛函數的重載。
    ~B(){
        cout<<"析構B"<<endl;
    }
};
int main(){
    B m;
    A *p=&m;
        //p->b=3/錯誤,指向派生類的基類指針不能調用派生類中的成員,只能調用基類中的成員,
        //除非該成員是虛函數
    p->f();W//調用派生類中的函數f

        //p->f(4);錯誤,注意這里不是在調用派生類中帶一個形參的f函數,因為帶一個參數的f函數不是虛函數,       

      //用指向派生類的基類指針時不會調用派生類中的函數,除非這個函數是虛函數。這里基類中沒有定義這種        //帶一個形參的f函數,所以這時會出現錯誤。

    p->A::f(); W//調用基類的虛函數f,可以用作用域運算符使用指向派生類的基類指針調用基類的虛函數

    p->h();//調用派生類中的虛函數版本h,用指向派生類的基類指針調用虛函數時派生類中的虛函數的默認值在這里不起作用。雖然派生類中的虛函數需要一個參數,

             //但這里不給參數仍是調用的派生類的帶兩個參數的虛函數h,而不是調用派生類中的不帶參數的h函數

    m.h(); //調用派生類中不帶參數的h函數,如果要用對象調用派生類中帶兩個形參的h函數,在本例中必須使用一個實參值。
    m.h(1);//調用派生類中帶兩個形參的h函數,用對象調用派生類中的虛函數時函數的默認值不受基類虛函數默認值的影響
    m.A::h(1);// 調用基類中的虛函數h.
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: