在決定寫這本書的時候我面臨的一個最大的問題是:是否把C++加入到討論
中去。盡管我熟悉C++,但是我不得不用C 和匯編來寫幾乎所有我的嵌入式軟件。而且在嵌入式軟件界對于C++是否值得所產生的性能損失的問題存有很大的爭議。一般認為C++程序會產生更大的代碼,這些代碼執行起來比完全用C寫的程序要慢。然而,C++給于程序員很多好處,并且我想在這本書中討論一些這樣的好處。因此,我最終決定把C++加入到討論中來,但是在我的例子中只是使用那些性能損失最小的特性。
我相信很多的讀者在他們自己的嵌入式系統編程的時候會面對相同的問題。在結束這本書之前。我想簡單地評判一下每一種我使用過的C++特性。并且提醒你一些我沒有使用過的比較昂貴的特性。
當然,并不是每一件C++引入的事情都是昂貴的。很多老的C++tb編譯器并入了一個叫作C.front 的技術,這項技術把C++的程序變成C,并且把結果供給標準的C 編譯器。這個事實暗示這兩種語言之間的句法差別很小,或與運行代價無關(注2)。只有最新的C++特性,如模板,不能夠用這種方式處理。
比如,類的定義是完全有益的。公有和私有成員數據及函數的列表與一個struct 及函數原型的列表沒有大的差別。然而,C++編譯器能夠用public 和private 關鍵字決定,哪一個方法調用和數據訪問是允許的或者是不允許的。因為這個決定在編譯的時候完成,所以運行時不會付出代價。單純的加入類既不會影響代碼的大小,又不會影響你的程序的效率。
默認參數值也是沒有損失的。編譯器只是加入代碼使得在每次函數被無參數調用的時候傳遞一個默認的值。類似地,函數名的重載也是編譯時的修改。具有相同名字但是不同參數的函數在編譯過程中分別分配了一個唯一的名字。每次函數名出現在程序中的時候編譯器就替換它,然后連接器正確的把它們匹配起來。我沒有在我的例子中使用C++的這一特性,但是我這幺做過而沒有影響性能。
操作符的重載是另一個我使用過但是沒有包括在例子中的特性。無論何時編譯器見到這樣一個操作符,它只是用合適的函數調用來替換它。因此,在下面列出的代碼,最后兩行是等價的,性能的損失很容易明白:
Complex a, b, c;
c = operator+(a, b)
// The traditional way: Function Call
// The C++ way: Operator Overloading
構造函數和析構函數也有一點與它們相關的損失。這些特殊的方法去分別保證每次這種類型的tbw對象在創建或者超出了范圍時被調用。然而,這個小量的開銷是為減少錯誤而支付的一個合理代價。構造函數完整地刪除了一個C 語言編程中與未初始化數據結構編程錯誤有關的類。這個特性也被證明是有用的,因為她隱藏了那些與像Timer 和Task 這樣復雜的類相關的笨拙初始化順序。
虛擬函數也具有一個合理的代價收益比。不要深究太多的關于什么是虛擬函數的細節,讓我們只是說一下沒有它們多態性就是不可能的。而沒有多態性,C++就不可能是一個真正的面向對象的語言。虛擬函數唯一一個明顯的代價是在調用虛擬函數之前附加了一個存儲查詢。普通的函數和方法調用是不受影響的。就我的體驗來說太昂貴的 C++特性有模板、異常事件及運行類型識別。這三個特性都對代碼的大小有負面的影響,而且異常事件和運行時類型識別還會增加執行時間。在決定是否使用這些特性之前,你可能要做一些實驗來看看它們會怎么樣影響你自己的應用程序的大小及速度。