Item M3 不要對陣列使用多型 無責任書評

2021-06-25 21:42:37 字數 2337 閱讀 1335

** 

其實是《more effective c++》書上的,並非原文照搬,而是刪減無關的內容,補充了一些事實和看法。

類繼承的最重要的特性是你可以通過基類指標或引用來操作派生類。這樣的指標或引用具有行為的多型性,就好像它們同時具有多種形態。c++允許你通過基類指標和引用來操作派生類陣列。不過這根本就不是乙個特性,因為這樣的**幾乎從不如你所願地那樣執行。假設你有乙個類bst(比如是搜尋樹物件)和繼承自bst 類的派生類balancedbst:

class bst ;

class balancedbst: public bst ;

在乙個真實的程式裡,這樣的類應該是模板類,但是在這個例子裡並不重要,加上模板只會使得**更難閱讀。為了便於討論,我們假設bst 和balancedbst 只包含int 型別資料。有這樣乙個函式,它能列印出bst 類陣列中每乙個bst 物件的內容:

void printbstarray(ostream& s, 

const bst array, 

int numelements)

//過載了操作符<< }

當你傳遞給該函式乙個含有bst 物件的陣列變數時,它能夠正常執行:

bst bstarray[10];

...printbstarray(cout, bstarray, 10); // 執行正常

然而,請考慮一下,當你把含有balancedbst 物件的陣列變數傳遞給printbstarray

函式時,會產生什麼樣的後果:

balancedbst bbstarray[10];

...printbstarray(cout, bbstarray, 10); // 還會執行正常麼?

你的編譯器將會毫無警告地編譯這個函式,但是再看一下這個函式的迴圈**:

for (int i = 0; i < numelements; i++) 

這裡的array[i]只是乙個指標演算法的縮寫:它所代表的是*(array+i)。我們知道array是乙個指向陣列起始位址的指標,但是array 中各元素記憶體位址與陣列的起始位址的間隔究竟有多大呢?它們的間隔是i*sizeof(乙個在陣列裡的物件),因為在array 陣列[0]到[i]間有i 個物件。編譯器為了建立正確遍歷陣列的執行**,它必須能夠確定陣列中物件的大小,這對編譯器來說是很容易做到的。引數array 被宣告為bst 型別,所以array 陣列中每乙個元素都是bst 型別,因此每個元素與陣列起始位址的間隔是i*sizeof(bst)。

至少你的編譯器是這麼認為的。但是如果你把乙個含有balancedbst 物件的陣列變數傳遞給printbstarray 函式,你的編譯器就會犯錯誤。在這種情況下,編譯器原先已經假設陣列中元素與bst 物件的大小一致,但是現在陣列中每乙個物件大小卻與balancedbst 一致。派生類的長度通常都比基類要長。我們料想balancedbst 物件長度的比bst 長。如果如此的話,printbstarray 函式生成的指標演算法將是錯誤的,沒有人知道如果用balancedbst 陣列來執行printbstarray 函式將會發生什麼樣的後果。不論是什麼後果都是令人不愉快的。

同樣,如果你試圖刪除乙個含有派生類物件的陣列,將會發生各種各樣的問題。以下是一種你可能採用的但不正確的做法。

//刪除乙個陣列, 但是首先記錄乙個刪除資訊

void deletearray(ostream& logstream, bst array)

balancedbst *baltreearray = 

new balancedbst[50];

// 建立乙個balancedbst 物件陣列

...deletearray(cout, baltreearray); // 記錄這個刪除操作

這裡面也掩藏著你看不到的指標演算法。當乙個陣列被刪除時,每乙個陣列元素的析構函

數也會被呼叫。當編譯器遇到這樣的**:

delete array;

它肯定象這樣生成**:

// 以與構造順序相反的順序來

// 解構array 陣列裡的物件

for ( int i = 陣列元素的個數1; i >= 0;--i)

因為你所編寫的迴圈語句根本不能正確執行,所以當編譯成可執行**後,也不可能正常執行。

語言規範中說通過乙個基類指標來刪除乙個含有派生類物件的陣列,結果將是不確定的。

這實際意味著執行這樣的**肯定不會有什麼好結果。多型和指標演算法不能混合在一起來用,所以陣列與多型也不能用在一起。

對於這一行為,我表示可惜和失望,畢竟這不是程式設計師想要的行為,但是也有用指標陣列或者別的設計?

有意思的是在

vc++中,能夠有正確的結果,雖然這樣不符合上文提到的語言規範(考慮到這本書的編寫日期,可能不是指最新的c++語言規範,但是不知道最新的規範對此有無改動),卻是想要的結果。但是g++就不這樣了。

不要對銷售有情緒

不要對銷售有情緒,如同我們不能對客戶有情緒一樣。職場上,在所有非銷售人員眼中,銷售就是客戶,銷售是我們通向客戶的紐帶。不論何種原因,我們都不能對銷售有情緒。1 對銷售有情緒只能說明我們還不成熟,生意思維不夠。銷售是幫我們做生意的,利益是銷售的世界語。沒有情緒。2 對銷售有情緒,很可能是中了銷售的圈套...

不要對陣列使用多型

不要對陣列使用多型 類繼承的最重要的特性是你可以通過基類指標或引用來操作派生類。這樣的指標或引用具有行為的多型性,就好像它們同時具有多種形態。c 允許你通過基類指標和引用來操作派生類陣列。不過這根本就不是乙個特性,因為這樣的 幾乎從不如你所願地那樣執行。假設你有乙個類bst 比如是搜尋樹物件 和繼承...

控制情緒,不要對孩子發火

今天早上女兒不吃早餐,我生氣了。我又把情緒傳給了兒子。現在想想自己真是應該好好控制一下情緒。女兒發脾氣,我應該教她控制情緒,解決問題,而不是吼她。下面這個學習材料寫得好,轉給大家一起學習 情緒管理 utm medium distribute.pc search result.none task bl...