C++程式設計最佳實踐(轉)
隨著計算機語言的發展,我們現在編寫一個程式越來越容易了。利用一些軟體開發工具,往往只要透過滑鼠的拖拖點點,計算機就會自動幫你生成許多程式碼。但在很多時候,計算機的這種能力被濫用了,我們往往只考慮把這個程式搭起來,而不去考慮程式的效能如何,程式是否足夠的健壯。而此節課的目的主要是介紹一些編碼的經驗,讓大家編寫的程式更加健壯和高效能。
1、Prefer const and inline to #define
在C++程式設計中應該儘量使用const和inline來代替#define,儘量做到能不用#define就不用。#define常見的用途有“定義常量”以及“定義宏”,但其中存在諸多的弊病。
第一,查錯不直觀,不利於除錯。Define的定義是由預處理程式處理的,作的是完全的文字替換,不做任何的型別檢查。在編譯器處理階段,define定義的東西已經被完全替換了,這樣在debug的時候就看不到任何的相關資訊,即跟蹤時不能step into宏。例如,把ASPECT_RATIO用define定義成1.653,編譯器就看不到ASPECT_RATIO這個名字了。如果編譯器報1.653錯,那麼就無從知道此1.653來自於何處。在真正編碼的時候應該使用如下的語句來定義:
第二,沒有任何型別資訊,不是type safe。因為它是文字級別的替換,這樣不利於程式的維護。
第三,define的使用很容易造成汙染。比如,如果有兩個標頭檔案都定義了ASPECT_RATIO, 而一個CPP檔案又同時包含了這兩個標頭檔案,那麼就會造成衝突。更難查的是另外一種錯誤,比如有如下的程式碼:
在.h檔案中Orange被定義成水果的一種,而在.cpp檔案中Orange又成為了一種顏色,那麼編譯器就會把此處的Orange替換成2,編譯可能仍然可以透過,程式也能夠執行,但是這就成了一個bug,表現出古怪的錯誤,且很難查錯。再比如定義了一個求a與b哪個數大的宏,#define max(a,b) ((a) > (b) ? (a) : (b))
在上面的操作中,max(++ a, b); 語句中a被++了兩次,而max(++ a, b + 10); 語句中a只加了一次,這樣在程式處理中就很有可能成為一個bug,且此bug也非常的難找。在實際編碼時可以使用如下的語句來做:
2、Prefer C++-style casts
在程式中經常會需要把一種型別轉換成另外一種型別,在C++中應該使用static_cast、const_cast、dynamic_cast、reinterpret_cast關鍵字來做型別轉換。因為這有以下好處,一是其本身就是一種註釋,在程式碼中看到上面這些關鍵字就可馬上知道此處是進行型別轉換。二是C語言中型別轉換通常是很難進行搜尋的,而透過關鍵字cast則可以很容易的找到程式中出現型別轉換的地方了。
3、Distinguish between prefix and postfix forms of increment and decrement operators
通常對於作業系統或編譯器自身支援的型別,prefix(字首,如++i)與postfix(字尾,如i++)的效果是一樣的。因為現在的編譯器都很聰明,它會自動做最佳化,這兩者的彙編程式碼是一樣的,效能不會有差別。但有時候也會有不同的,如一些過載了運算子的型別。下面是模擬prefix與postfix的操作過程,可以發現在postfix操作中會生成一個臨時變數,而這一臨時變數是會佔用額外的時間和開銷的。
1、Prefer const and inline to #define
在C++程式設計中應該儘量使用const和inline來代替#define,儘量做到能不用#define就不用。#define常見的用途有“定義常量”以及“定義宏”,但其中存在諸多的弊病。
第一,查錯不直觀,不利於除錯。Define的定義是由預處理程式處理的,作的是完全的文字替換,不做任何的型別檢查。在編譯器處理階段,define定義的東西已經被完全替換了,這樣在debug的時候就看不到任何的相關資訊,即跟蹤時不能step into宏。例如,把ASPECT_RATIO用define定義成1.653,編譯器就看不到ASPECT_RATIO這個名字了。如果編譯器報1.653錯,那麼就無從知道此1.653來自於何處。在真正編碼的時候應該使用如下的語句來定義:
static const double ASPECT_RATIO = 1.653; |
第二,沒有任何型別資訊,不是type safe。因為它是文字級別的替換,這樣不利於程式的維護。
第三,define的使用很容易造成汙染。比如,如果有兩個標頭檔案都定義了ASPECT_RATIO, 而一個CPP檔案又同時包含了這兩個標頭檔案,那麼就會造成衝突。更難查的是另外一種錯誤,比如有如下的程式碼:
// in header file def.h #define Apple 1 #define Orange 2 #define Pineapple 3 … // in some cpp file that includes the def.h enum Colors {White, Black, Purple, Orange}; |
在.h檔案中Orange被定義成水果的一種,而在.cpp檔案中Orange又成為了一種顏色,那麼編譯器就會把此處的Orange替換成2,編譯可能仍然可以透過,程式也能夠執行,但是這就成了一個bug,表現出古怪的錯誤,且很難查錯。再比如定義了一個求a與b哪個數大的宏,#define max(a,b) ((a) > (b) ? (a) : (b))
int a = 5, b = 0; max(++ a, b); max(++ a, b + 10); |
在上面的操作中,max(++ a, b); 語句中a被++了兩次,而max(++ a, b + 10); 語句中a只加了一次,這樣在程式處理中就很有可能成為一個bug,且此bug也非常的難找。在實際編碼時可以使用如下的語句來做:
template inline const T& max(const T& a, const T& b) { return a > b ? a : b; } |
2、Prefer C++-style casts
在程式中經常會需要把一種型別轉換成另外一種型別,在C++中應該使用static_cast、const_cast、dynamic_cast、reinterpret_cast關鍵字來做型別轉換。因為這有以下好處,一是其本身就是一種註釋,在程式碼中看到上面這些關鍵字就可馬上知道此處是進行型別轉換。二是C語言中型別轉換通常是很難進行搜尋的,而透過關鍵字cast則可以很容易的找到程式中出現型別轉換的地方了。
3、Distinguish between prefix and postfix forms of increment and decrement operators
通常對於作業系統或編譯器自身支援的型別,prefix(字首,如++i)與postfix(字尾,如i++)的效果是一樣的。因為現在的編譯器都很聰明,它會自動做最佳化,這兩者的彙編程式碼是一樣的,效能不會有差別。但有時候也會有不同的,如一些過載了運算子的型別。下面是模擬prefix與postfix的操作過程,可以發現在postfix操作中會生成一個臨時變數,而這一臨時變數是會佔用額外的時間和開銷的。
// prefix form: increment and fetch UPInt& UPInt::operator++() { *this += 1; // increment return *this; // fetch } // postfix form: fetch and increment const UPInt UPInt::operator++(int) { UPInt oldValue = *this; // fetch ++(*this); // increment return oldValue; // return what was fetched } |
一般情況下不需要區分是先++,還是後++,但是我們在編寫程式的時候最好能習慣性的將其寫成++i的形式,如在使用STL中的iterator時,prefix與postfix會有相當大的效能差異。請不要小看這些細節,實際在編寫程式的時候,若不注意具體細節,你會發現程式的效能會非常的低。但要注意,雖然在大多數情況下可以用prefix來代替postfix,但有一種情況例外,那就是有[]運算子時,比如gzArray [++index] 是不等於 gzArray[index++]的。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10172717/viewspace-928854/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Python程式設計規範+最佳實踐Python程式設計
- Laravel最佳實踐–事件驅動程式設計Laravel事件程式設計
- Laravel 最佳實踐 -- 事件驅動程式設計Laravel事件程式設計
- Laravel最佳實踐 -- 事件驅動程式設計Laravel事件程式設計
- 函數語言程式設計最佳實踐函數程式設計
- Java程式設計師的八個最佳實踐Java程式設計師
- MaxCompute表設計最佳實踐
- TypeScript 資料模型層程式設計的最佳實踐TypeScript模型程式設計
- [01] C#網路程式設計的最佳實踐C#程式設計
- 領域驅動設計最佳實踐--程式碼篇
- react 設計模式與最佳實踐React設計模式
- 設計微服務的最佳實踐微服務
- C++程式設計實現C++程式設計
- vSAN 設計、部署、運維最佳實踐運維
- Go程式設計實踐Go程式設計
- C++程式設計規範-101條規則準則與最佳實踐電子書pdf下載C++程式設計
- 13 個設計 REST API 的最佳實踐RESTAPI
- 高效前端程式設計實踐前端程式設計
- hadoop程式設計實踐(一)Hadoop程式設計
- C++高階語言程式設計案例與實踐輔導pdfC++程式設計
- 鴻蒙程式設計江湖:ArkTS開發綜合案例與最佳實踐鴻蒙程式設計
- Spring Boot中五個設計模式最佳實踐Spring Boot設計模式
- 敏捷最佳實踐:設計衝刺完整指南 -Useberry敏捷
- 資料庫設計的十個最佳實踐資料庫
- Java併發程式設計實踐Java程式設計
- framebuffer應用程式設計實踐程式設計
- Golang 併發程式設計實踐Golang程式設計
- 一些通過SAPABAP程式碼審查得出的ABAP程式設計最佳實踐程式設計
- Node之道:設計、架構和最佳實踐 | Alex Kondov架構
- 設計出色API的最佳實踐與原則 - JamesAPI
- 異常值檢測!最佳統計方法實踐(程式碼實現)!⛵
- C++程式設計基礎實驗1C++程式設計
- c++程式設計基礎實驗4C++程式設計
- 一些通過SAP ABAP程式碼審查得出的ABAP程式設計最佳實踐程式設計
- [分享]2021 年對 React 前端程式設計師的 10 個程式碼最佳實踐建議React前端程式設計師
- Python將字串轉為字典最佳實踐Python字串
- 詞頻統計任務程式設計實踐程式設計
- 實驗 2 Scala 程式設計初級實踐程式設計
- 阿里研究員谷樸:API 設計最佳實踐的思考阿里API