MoreEffectiveC++:不使用多態(tài)性數(shù)組

字號:

類繼承的最重要的特性是你可以通過基類指針或引用來操作派生類。這樣的指針或引用具有行為的多態(tài)性,就好像它們同時具有多種形態(tài)。C++允許你通過基類指針和引用來操作派生類數(shù)組。不過這根本就不是一個特性,因?yàn)檫@樣的代碼根本無法如你所愿地那樣運(yùn)行。
    假設(shè)你有一個類BST(比如是搜索樹對象)和繼承自BST類的派生類BalancedBST:
    class BST { ... };
    class BalancedBST: public BST { ... };
    在一個真實(shí)的程序里,這樣的類應(yīng)該是模板類,但是在這個例子里并不重要,加上模板只會使得代碼更難閱讀。為了便于討論,我們假設(shè)BST和BalancedBST只包含int類型數(shù)據(jù)。
    有這樣一個函數(shù),它能打印出BST類數(shù)組中每一個BST對象的內(nèi)容:
    void printBSTArray(ostream& s,
    const BST array[],
    int numElements)
    {
    for (int i = 0; i < numElements; ) {
    s << array[i]; //假設(shè)BST類
    } //重載了操作符<<
    }
    當(dāng)你傳遞給該函數(shù)一個含有BST對象的數(shù)組變量時,它能夠正常運(yùn)行:
    BST BSTArray[10];
    ...
    printBSTArray(cout, BSTArray, 10); // 運(yùn)行正常
    然而,請考慮一下,當(dāng)你把含有BalancedBST對象的數(shù)組變量傳遞給printBSTArray函數(shù)時,會產(chǎn)生什么樣的后果:
    BalancedBST bBSTArray[10];
    ...
    printBSTArray(cout, bBSTArray, 10); // 還會運(yùn)行正常么?
    你的編譯器將會毫無警告地編譯這個函數(shù),但是再看一下這個函數(shù)的循環(huán)代碼:
    for (int i = 0; i < numElements; ) {
    s << array[i];
    }
    這里的array[I]只是一個指針?biāo)惴ǖ目s寫:它所代表的是*(array)。我們知道array是一個指向數(shù)組起始地址的指針,但是array中各元素內(nèi)存地址與數(shù)組的起始地址的間隔究竟有多大呢?它們的間隔是i*sizeof(一個在數(shù)組里的對象),因?yàn)樵赼rray數(shù)組[0]到[I]間有I個對象。編譯器為了建立正確遍歷數(shù)組的執(zhí)行代碼,它必須能夠確定數(shù)組中對象的大小,這對編譯器來說是很容易做到的。參數(shù)array被聲明為BST類型,所以array數(shù)組中每一個元素都是BST類型,因此每個元素與數(shù)組起始地址的間隔是be i*sizeof(BST)。
    至少你的編譯器是這么認(rèn)為的。但是如果你把一個含有BalancedBST對象的數(shù)組變量傳遞給printBSTArray函數(shù),你的編譯器就會犯錯誤。在這種情況下,編譯器原先已經(jīng)假設(shè)數(shù)組中元素與BST對象的大小一致,但是現(xiàn)在數(shù)組中每一個對象大小卻與BalancedBST一致。派生類的長度通常都比基類要長。我們料想BalancedBST對象長度的比BST長。如果如此的話,printBSTArray函數(shù)生成的指針?biāo)惴▽⑹清e誤的,沒有人知道如果用BalancedBST數(shù)組來執(zhí)行printBSTArray函數(shù)將會發(fā)生什么樣的后果。不論是什么后果都是令人不愉快的。