C++輔導(dǎo):C++函數(shù)虛函數(shù)功能失效?

字號(hào):

在C++的子類中,定義某成員函數(shù)時(shí),我們通常需要顯式的調(diào)用其基類的版本。例如在一個(gè)繪圖類結(jié)構(gòu)中,子類只需要去繪制在子類添加進(jìn)去的部分圖形,然后再調(diào)用基類去完成基礎(chǔ)的圖形。這個(gè)成員函數(shù)有一般都是虛函數(shù)。對于構(gòu)造函數(shù),在子類的構(gòu)造函數(shù)中也可能顯式地執(zhí)行基類的構(gòu)造函數(shù)。
    先看看一個(gè)例子,基類Shape的默認(rèn)構(gòu)造函數(shù)不分配name空間,但子類Line的默認(rèn)構(gòu)造函數(shù)會(huì)按照規(guī)則自動(dòng)產(chǎn)生name,這里我們假設(shè)name是private的,如果name不是private,問題會(huì)很簡單,也不會(huì)出現(xiàn)下述問題了。但在現(xiàn)實(shí)中,通常在基類的構(gòu)造函數(shù)會(huì)初使化一些重要的private成員,或者構(gòu)造函數(shù)比較長,在子類中不想復(fù)制這些代碼而希望直接調(diào)用到基類的構(gòu)造函數(shù)。一般我們可以在初使化式中直接構(gòu)造基類,但有的時(shí)候,需要先計(jì)算出基類構(gòu)造函數(shù)的參數(shù),如同本例中一樣需要先產(chǎn)生一個(gè)autoName。
    class Shape{
    public:
    Shape(LPCTSTR name){
    this->name = new TCHAR[lstrlen(name) + 1];
    lstrcpy(this->name, name);
    }
    Shape(){
    name = NULL;
    }
    virtual void draw() = 0;
    LPCTSTR getName() const{ return name;};
    private:
    LPTSTR name;
    };
    class Line:public Shape{
    public:
    static int autoIdx;
    Line(){
    LPTSTR autoName = new TCHAR[32];
    memset(autoName, 0, 32 * sizeof(TCHAR));
    lstrcpy(autoName, L"NewLine");
    _itow_s(autoIdx++, autoName + lstrlen(autoName), 8, 10);
    this->Shape::Shape(autoName);
    }
    Line(LPCTSTR name):Shape(name){
    }
    void draw(){
    }
    };
    int Line::autoIdx = 1;
    int _tmain(int argc, _TCHAR* argv[])
    {
    Shape* l = new Line();
    l->draw();
    }
    運(yùn)行該程序,按理來說應(yīng)該是沒有什么問題,但實(shí)際出現(xiàn)的錯(cuò)誤還是讓人丈二摸不著頭腦。
    
    一開始我一直以為是不是draw()方法與基類的名稱不一致,反復(fù)的拷貝,比較參數(shù)與返回值,但一直彈出上面的錯(cuò)誤。因?yàn)槊髅髟贚ine類定義了draw()方法,怎么會(huì)調(diào)用得到Shape的純虛函數(shù)呢?折騰了近兩個(gè)小時(shí),沒轍了,只好注釋代碼一行一行地排查,最后發(fā)現(xiàn),當(dāng)將
    this->Shape::Shape(autoName);
    Examda提示: 注掉之后,這個(gè)問題便不存在了??磥韱栴}就一直出在上面的這行代碼上去了,仔細(xì)想想,想起虛函數(shù)表就是在構(gòu)造函數(shù)中初使化的,之所以一直調(diào)用基類的方法,一定就是在調(diào)用上面基類的構(gòu)造函數(shù)時(shí),之前已經(jīng)初使化好的虛函數(shù)指針被基類的虛函數(shù)指針覆蓋了。
    其實(shí)之所以做上面的調(diào)用,還是受到Java語言的影響,在Java中,無論在什么位置,直接用super()就搞定了。在C++中,虛函數(shù)成員這樣調(diào)用沒有問題,但在構(gòu)造函數(shù)中,還是會(huì)出現(xiàn)比較嚴(yán)重的運(yùn)行故障,如果要在C++編碼中不要再出現(xiàn)類似的問題,還是需要透徹了解在C++的構(gòu)造函數(shù)到底干了些什么,以及虛函數(shù)表,這個(gè)隱藏在背后的實(shí)現(xiàn)虛函數(shù)功能的機(jī)制。