主頁 > 軟體設計 > 萬字長文吐血整理C語言要點--《高質量的C/C++編程》林銳-精簡修真版。

萬字長文吐血整理C語言要點--《高質量的C/C++編程》林銳-精簡修真版。

2021-04-24 14:09:50 軟體設計

文章目錄

    • 《高質量的C/C++編程》
      • 一 著作權宣告
      • 二 做題點這里,考察質量,按答案嚴格打分,
      • 三 前言
        • 我的前言
        • 博士的前言總結
      • 第一二章:
        • 防止頭檔案被重復包含,
        • 目錄結構
        • 程式的版式追求清晰、美觀,是程式風格的重要構成因素,
        • 代碼行
        • 對齊
        • 長行拆分
        • 類的版式
      • 第三章:命名規則
        • Windows 簡單的命名規則
        • 簡單的 Unix 應用程式命名規則
      • 第 4 章 運算式和基本陳述句
        • 運算子的優先級
        • 復合運算式
        • if陳述句_本節以“與零值比較”為例,展開討論, _
          • 布爾變數與零值比較
          • 整型變數與零值比較
          • 浮點變數與零值比較
          • 指標變數與零值比較
        • 4.4 回圈陳述句的效率
        • 4.5 for 陳述句的回圈控制變數
        • 4.6 switch 陳述句
      • 第五章 常量
        • 5.1 為什么需要常量
        • 5.2 const 與 #define 的比較
        • 5.3 常量定義規則
        • 5.4 類中的常量
      • 第六章 函式設計
        • 這里有一個很細的講解
        • 引數的規則
        • 6.2 回傳值的規則
        • 6.3 函式內部實作的規則
        • 6.4 其他建議
        • 6.5 使用斷言
      • 第 7 章 記憶體管理
        • 7.3 指標與陣列的對比
        • 7.4 指標引數是如何傳遞記憶體的?
        • 7.5 free 和 delete 把指標怎么啦
        • 7.6 切記要初始化指標,釋放指標后要置成空指標,
        • 7.8 有了 malloc/free 為什么還要 new/delete
        • 7.9 記憶體耗盡怎么辦?
        • 試圖耗盡作業系統的記憶體
        • 7.10 malloc/free 的使用要點
        • 7.11 new/delete 的使用要點
        • 7.12 一些心得體會
      • 第 8 章 C++函式的高級特性
        • 8.1 函式多載的概念
        • 8.2 成員函式的多載、覆寫與隱藏、
        • 8.3 引數的預設值
        • 8.4 運算子多載
        • 8.5 函式行內
        • 8.6 一些心得體會
      • 第 9 章 類的建構式、解構式與賦值函式
      • 第 10 章 類的繼承與組合
      • 第 11 章 其它編程經驗
        • 使用 const 提高函式的健壯性
        • 提高程式的效率
        • 一些有益的建議
  • GG.本文結尾語:

《高質量的C/C++編程》

一 著作權宣告

上海貝爾網路應用-林銳
在這里插入圖片描述
在這里插入圖片描述

二 做題點這里,考察質量,按答案嚴格打分,

大俠!!點這里做題,!做完題記得回來哦~~!

三 前言

我的前言

目前還在C語言階段,大部分是C語言能用到的細節,看一遍總會忘,記得收藏~
C++復制粘貼了一部分,然而越看越吃力,就待定了…

博士的前言總結

  • (1)知錯就改;
  • (2)經常溫故而知新;
  • (3)堅持學習,天天向上,

第一二章:

防止頭檔案被重復包含,

- `ifndef famer_h`
	- define famer_h
	- ..
	- endif
  • <>頭檔案,編譯器將參考標準庫的頭檔案,從標準目錄開始搜索、
  • “”頭檔案,將從用戶的作業目錄開始搜索,用戶自己創建的頭檔案
    • 頭檔案只存放宣告,不存放定義
    • 不建議使用全域變數
    • 在這里插入圖片描述

目錄結構

  • 如果一個軟體的頭檔案數目比較多(如超過十個),通常應將頭檔案和定義檔案分
    • 別保存于不同的目錄,以便于維護,
    • 例如可將頭檔案保存于 include 目錄,將定義檔案保存于 source 目錄(可以是多級
    • 目錄),
    • 如果某些頭檔案是私有的,它不會被用戶的程式直接參考,則沒有必要公開其“宣告”,為了加強資訊隱藏,這些私有的頭檔案可以和定義檔案存放于同一個目錄

程式的版式追求清晰、美觀,是程式風格的重要構成因素,

  • 空行起著分隔程式段落的作用,
    • 空行不會浪費記憶體
    • 【規則 2-1-1】在每個類宣告之后、每個函式定義結束之后都要加空行,
    • 【規則 2-1-2】在一個函式體內,邏揖上密切相關的陳述句之間不加空行,其它地方應加空行分隔,
    • 在這里插入圖片描述

代碼行

  • 一行代碼只做一件事情,如只定義一個變數,或只寫一條陳述句,這樣的代碼容易閱讀,并且方便于寫注釋,
    • if、for、while、do 等陳述句自占一行,執行陳述句不得緊跟其后,
    • 不論執行陳述句有多少都要加{},這樣可以防止書寫失誤,
    • 關鍵字之后要留空格,象 const、virtual、inline、case 等關鍵字之后至少要留一個空格,否則無法辨析關鍵字,象 if、for、while 等關鍵字之后應留一個空格再跟左括號‘(’,以突出關鍵字,
    • z 【規則 2-3-4】‘,’之后要留空格,如 Function(x, y, z),如果‘;’不是一行的結束符號,其后要留空格,如 for(initialization; condition; update),
    • z 【規則 2-3-5】賦值運算子、比較運算子、算術運算子、邏輯運算子、位域運算子,如“=”、“+=” “>=”、“<=”、“+”、“”、“%”、“&&”、“||”、“<<”,“^”等二元運算子的前后應當加空格,
    • 在這里插入圖片描述

對齊

  • z 【規則 2-4-1】程式的分界符‘{’和‘}’應獨占一行并且位于同一列,同時與參考它們的陳述句左對齊,
    • 【規則 2-4-2】{ }之內的代碼塊在‘{’右邊數格處左對齊
    • 在這里插入圖片描述

長行拆分

  • 【規則 2-5-1】代碼行最大長度宜控制在 70 至 80 個字符以內,代碼行不要過長,否則眼睛看不過來,也不便于列印,

    • 【規則 2-5-2】長運算式要在低優先級運算子處拆分成新行,運算子放在新行之首(以便突出運算子),拆分出的新行要進行適當的縮進,使排版整齊,陳述句可讀,
  • 【規則 2-6-1】應當將修飾符 * 和 & 緊靠變數名 *

    • 【規則 2-7-2】如果代碼本來就是清楚的,則不必加注釋,否則多此一舉,令人厭煩,注釋的花樣要少
      在這里插入圖片描述

類的版式

  • 類可以將資料和函式封裝在一起,其中函式表示了類的行為(或稱服務),類提供關鍵字 public、protected 和 private,分別用于宣告哪些資料和函式是公有的、受保護的或者是私有的,這樣可以達到資訊隱藏的目的,即讓類僅僅公開必須要讓外界知道的內容,而隱藏其它一切內容,我們不可以濫用類的封裝功能,不要把它當成火鍋,什么東西都往里扔,
    • (1)將 private 型別的資料寫在前面,而將 public 型別的函式寫在后面,“以資料為中心”,重點關注類的內部結構,
    • (2)將 public 型別的函式寫在前面,而將 private 型別的資料寫在后面,“以行為為中心”,重點關注的是類應該提供什么樣的介面(或服務),
    • 建議“以行為為中心”的書寫方式.
    • 在這里插入圖片描述

第三章:命名規則

  • 【規則 3-1-1】識別符號應當直觀且可以拼讀,可望文知意,不必進行“解碼”,識別符號最好采用英文單詞或其組合,便于記憶和閱讀,
  • 【規則 3-1-2】識別符號的長度應當符合“min-length && max-information”原則,
  • 【規則 3-1-3】命名規則盡量與所采用的作業系統或開發工具的風格保持一致

Windows 簡單的命名規則

  • Windows 應用程式的識別符號通常采用“大小寫”混排的方式,如 AddChild,

    • 而 Unix 應用程式的識別符號通常采用“小寫加下劃線”的方式,如 add_child,
  • 【規則 3-1-6】變數的名字應當使用“名詞”或者“形容詞+名詞”,

  • 【規則 3-1-7】全域函式的名字應當使用“動詞”或者“動詞+名詞”(動賓詞組),

  • 【規則 3-1-8】用正確的反義詞組命名具有互斥意義的變數或相反動作的函式等.

- 例如:int minValue; 
		- int maxValue;
		- int SetValue(); 
		- int GetValue()
  • 【規則 3-2-1】類名和函式名用大寫字母開頭的單詞組合而成,

  • 在這里插入圖片描述

  • 【規則 3-2-3】常量全用大寫的字母,用下劃線分割單詞,

  • 【規則 3-2-4】靜態變數加前綴 s_(表示 static),

  • 【規則 3-2-5】如果不得已需要全域變數,則使全域變數加前綴 g_(表示 global),
    -在這里插入圖片描述

  • 【規則 3-2-6】類的資料成員加前綴 m_(表示 member),這樣可以避免資料成員與_成員函式的引數同名,

  • 在這里插入圖片描述

    • 【規則 3-2-7】為了防止某一軟體庫中的一些識別符號和其它軟體庫中的沖突,可以為各種識別符號加上能反映軟體性質的前綴,例如三維圖形標準 OpenGL 的所有庫函式均以 gl 開頭,所有常量(或宏定義)均以 GL 開頭,

簡單的 Unix 應用程式命名規則

  • 這是個迷

第 4 章 運算式和基本陳述句

運算子的優先級

  • C++/C 語言的運算子有數十個,運算子的優先級與結合律如表 4-1 所示,注意一元運算子 + - * 的優先級高于對應的二元運算子,
    在這里插入圖片描述
    在這里插入圖片描述

  • 【規則 4-1-1】如果代碼行中的運算子比較多,用括號確定運算式的操作順序,避免使用默認的優先級,

- 例如:word = (high << 8) | low 
- if ((a | b) && (a & c)) 

復合運算式

  • 如 a = b = c = 0 這樣的運算式稱為復合運算式,允許復合運算式存在的理由是:(1)書寫簡潔;(2)可以提高編譯效率,但要防止濫用復合運算式,

  • z 【規則 4-2-1】不要撰寫太復雜的復合運算式,
    例如:
    i = a >= b && c < d && c + f <= g + h ; // 復合運算式過于復雜

  • z 【規則 4-2-2】不要有多用途的復合運算式,
    例如:d = (a = b + c) + r ;
    該運算式既求 a 值又求 d 值,應該拆分為兩個獨立的陳述句:

a = b + c; 
d = a + r;
  • 【規則 4-2-3】不要把程式中的復合運算式與“真正的數學運算式”混淆, 例如:
    if (a < b < c) // a < b < c 是數學運算式而不是程式運算式
    并不表示 if ((a<b) && (b<c))
    而是成了令人費解的 if ( (a<b)<c )

if陳述句_本節以“與零值比較”為例,展開討論, _

布爾變數與零值比較
  • 【規則 4-3-1】不可將布爾變數直接與 TRUE、FALSE 或者 1、0 進行比較,
    • 根據布爾型別的語意,零值為“假”(記為 FALSE),任何非零值都是“真”(記為TRUE),TRUE 的值究竟是什么并沒有統一的標準,例如 Visual C++ 將 TRUE 定義為1,而 Visual Basic 則將 TRUE 定義為-1,
    • 假設布爾變數名字為 flag,它與零值比較的標準 if 陳述句如下:
- if (flag)   表示 flag 為真 
- if (!flag)   表示 flag 為假
整型變數與零值比較
  • 【規則 4-3-2】應當將整型變數用“==”或“!=”直接與 0 比較,
    • 假設整型變數的名字為 value,它與零值比較的標準 if 陳述句如下:
- if (value == 0) 
- if (value != 0) 
浮點變數與零值比較
  • 【規則 4-3-3】不可將浮點變數用“==”或“!=”與任何數字比較,
    • 千萬要留意,無論是 float 還是 double 型別的變數,都有精度限制,
    • 所以一定要避免將浮點變數用“==”或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式,
    • 假設浮點變數的名字為 x,應當將 if (x == 0.0) 隱含錯誤的比較
- 轉化為 if ((x>=-EPSINON) && (x<=EPSINON)) 
			- 其中 EPSINON 是允許的誤差(即精度),
指標變數與零值比較
  • 【規則 4-3-4】應當將指標變數用“==”或“!=”與 NULL 比較,指標變數的零值是“空”(記為 NULL
    • 盡管 NULL 的值與 0 相同但是兩者意義不同,假設指標變數的名字為 p,它與零值比較的標準 if 陳述句如下:
- if (p == NULL) // p 與 NULL 顯式比較,強調 p 是指標變數  if (p != NULL) 

4.4 回圈陳述句的效率

  • C++/C 回圈陳述句中,for 陳述句使用頻率最高,while 陳述句其次,do 陳述句很少用,

    • 本節重點論述回圈體的效率,提高回圈體效率的基本辦法是降低回圈體的復雜性,
  • 【建議 4-4-1】在多重回圈中,如果有可能,應當將最長的回圈放在最內層,最短的回圈放在最外層,以減少 CPU 跨切回圈層的次數,

- for (col=0; col<5; col++ ) 
- {
	-  for (row=0; row<100; row++) 
	- { 
		- sum = sum + a[row][col]; 
	- } 
- } 
  • 【建議 4-4-2】如果回圈體記憶體在邏輯判斷,并且回圈次數很大,宜將邏輯判斷移到回圈體的外面,
    • 示例 4-4?的程式比示例 4-4(d)多執行了 N-1 次邏輯判斷,并且由于前者老要進行邏輯判斷,打斷了回圈“流水線”作業,使得編譯器不能對回圈進行優化處理,降低了效率,
- for (i=0; i<N; i++)  *C
- { 
	- if (condition) 
		- DoSomething(); 
	- else 
		- DoOtherthing(); 
- } 
- if (condition)   *D
- { 
	- for (i=0; i<N; i++) 
		- DoSomething(); 
- } 
- else 
- { 
	- for (i=0; i<N; i++) 
		- DoOtherthing(); 
- } 

4.5 for 陳述句的回圈控制變數

  • 【規則 4-5-1】不可在 for 回圈體內修改回圈變數,防止 for 回圈失去控制,

4.6 switch 陳述句

  • switch 是多分支選擇陳述句,而 if 陳述句只有兩個分支可供選擇,
    • 雖然可以用嵌套的if 陳述句來實作多分支選擇,但那樣的程式冗長難讀,這是 switch 陳述句存在的理由,
- switch (variable) 
- { 
	- case value1 :- break; 
	- case value2 :- break; 
	-.
	- default :- break; 
- }
  • 【規則 4-6-1】每個 case 陳述句的結尾不要忘了加 break,否則將導致多個分支重疊(除非有意使多個分支重疊),
  • 【規則 4-6-2】不要忘記最后那個 default 分支,即使程式真的不需要 default 處理,也應該保留陳述句 default : break; 這樣做并非多此一舉,而是為了防止別人誤以為你忘了 default 處理,

第五章 常量

5.1 為什么需要常量

  • 如果不使用常量,直接在程式中填寫數字或字串,將會有什么麻煩?
    • (1) 程式的可讀性(可理解性)變差,程式員自己會忘記那些數字或字符什么意思,用戶則更加不知它們從何處來、表示什么,
    • (2) 在程式的很多地方輸入同樣的數字或字串,難保不發生書寫錯誤,
    • (3) 如果要修改數字或字串,則會在很多地方改動,既麻煩又容易出錯,
    • 在這里插入圖片描述

5.2 const 與 #define 的比較

  • (1) const 常量有資料型別,而宏常量沒有資料型別,編譯器可以對前者進行型別安全檢查,而對后者只進行字符替換,沒有型別安全檢查,并且在字符替換可能會產生意料不到的錯誤(邊際效應),

  • (2) 有些集成化的除錯工具可以對 const 常量進行除錯,但是不能對宏常量進行除錯,

  • 【規則 5-2-1】在 C++ 程式中只使用 const 常量而不使用宏常量,即 const 常量完全取代宏常量,

5.3 常量定義規則

  • 【規則 5-3-1】需要對外公開的常量放在頭檔案中,不需要對外公開的常量在定義檔案的頭部,為便于管理,可以把不同模塊的常量集中存放在一個公共的頭檔案中,
  • 【規則 5-3-2】如果某一常量與其它常量密切相關,應在定義中包含這種關系,而不應給出一些孤立的值,
    • 例如:
- const float RADIUS = 100; 
- const float DIAMETER = RADIUS * 2;

5.4 類中的常量

  • 列舉常量不會占用物件的存盤空間,它們在編譯時被全部求值,列舉常量的缺點是:它的隱含資料型別是整數,其最大值有限,且不能表示浮點數(如 PI=3.14159),
  • 在這里插入圖片描述

第六章 函式設計

這里有一個很細的講解

【C語言從青銅到王者】第二篇·詳解函式

  • C 語言中,函式的引數和回傳值的傳遞方式有兩種
    • 值傳遞(pass by value)和
    • 指標傳遞(pass by pointer),
  • C++ 語言中多了參考傳遞(pass by reference),由于參考傳遞的性質象指標傳遞,而使用方式卻象值傳遞,

引數的規則

  • 【規則 6-1-1】引數的書寫要完整,不要貪圖省事只寫引數的型別而省略引數名字,如果函式沒有引數,則用 void 填充
  • 【規則 6-1-2】引數命名要恰當,順序要合理,
    • void StringCopy( char *strDestination,char *strSource);
  • 【規則 6-1-3】如果引數是指標,且僅作輸入用,則應在型別前加 const,以防止該指標在函式體內被意外修改,
  • void StringCopy(char *strDestination,const char *strSource);
  • 【規則 6-1-4】如果輸入引數以值傳遞的方式傳遞物件,則宜改用“const &”方式來傳遞,這樣可以省去臨時物件的構造和析構程序,從而提高效率,

6.2 回傳值的規則

  • 【規則 6-2-1】不要省略回傳值的型別

    • C 語言中,凡不加型別說明的函式,一律自動按整型處理,這樣做不會有什么好處,卻容易被誤解為 void 型別
    • C++語言有很嚴格的型別安全檢查,不允許上述情況發生,由于C++程式可以呼叫C 函式,為了避免混亂,規定任何 C++/ C 函式都必須有型別,
    • 如果函式沒有回傳值,那么應宣告為 void 型別
  • 如果 getchar 碰到檔案結束標志或發生讀錯誤,它必須回傳一個標志 EOF,為了區別于正常的字符,只好將 EOF 定義為負數(通常為負 1),因此函式 getchar 就成了 int 型別,

  • 在這里插入圖片描述

6.3 函式內部實作的規則

  • 函式的出口入口處對引數的有效性進行檢查

  • 在這里插入圖片描述

  • 編譯器直接把臨時物件創建并初始化在外部存盤單元中,省去了拷貝和析構的化費,提高了效率,

  • 在這里插入圖片描述

6.4 其他建議

  • 【建議 6-4-1】函式的功能要單一,不要設計多用途的函式,
  • 【建議 6-4-2】函式體的規模要小,盡量控制在 50 行代碼之內,
  • 建議 6-4-3】盡量避免函式帶有“記憶”功能,如:static

6.5 使用斷言

  • 程式一般分為 Debug 版本和 Release 版本,Debug 版本用于內部除錯,Release 版本發行給用戶使用,
  • 斷言 assert 是僅在 Debug 版本起作用的宏,它用于檢查“不應該”發生的情況,
  • 而是宏,程式員可以把assert 看成一個在任何系統狀態下都可以安全使用的無害測驗手段,
  • 在這里插入圖片描述

第 7 章 記憶體管理

  • 640K ought to be enough for everybody — Bill Gates 1981

  • 記憶體分配方式有三種:

    • (1) 從靜態存盤區域分配,記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個運行期間都存在,例如全域變數,static 變數,
    • (2) 在堆疊上創建,在執行函式時,函式內區域變數的存盤單元都可以在堆疊上創建,函式執行結束時這些存盤單元自動被釋放,堆疊記憶體分配運算內置于處理器的指令集中,效率很高,但是分配的記憶體容量有限,
    • (3) 從堆上分配,亦稱動態記憶體分配,程式在運行的時候用 malloc 或 new 申請任意多少的記憶體,程式員自己負責在何時用 free 或 delete 釋放記憶體,動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多
  • 要對堆區開辟的記憶體判斷是否為NULL

    • 開辟的記憶體要進行初始化
    • 注意初始化成功時不要越界
    • 動態記憶體的申請與釋放必須配對,程式中 malloc 與 free 的使用次數一定要相同,
    • 注意不要回傳指向“堆疊記憶體”的“指標”
    • 將指標設定為 NULL

7.3 指標與陣列的對比

  • 陣列要么在靜態存盤區被創建(如全域陣列),要么在堆疊上被創建,陣列名對應著(而不是指向)一塊記憶體,其地址與容量在生命期內保持不變,只有陣列的內容可以改變,

  • 指標可以隨時指向任意型別的記憶體塊,它的特征是“可變”,所以我們常用指標來高質量 C++/C 編程指南,操作動態記憶體,指標遠比陣列靈活,但也更危險

  • 指標 p 指向常量字串“world”(位于靜態存盤區,內容為 world\0),
    常量字串的內容是不可以被修改的,從語法上看,編譯器并不覺得陳述句 p[0]= ‘X’有什么不妥,但是該陳述句企圖修改常量字串的內容而導致運行錯誤
    在這里插入圖片描述

  • 陣列之間比較用 strcmp ,賦值用strcpy,

  • 在這里插入圖片描述

  • sizeof(字串) ,’\0’也算字串大小,

7.4 指標引數是如何傳遞記憶體的?

  1. 用“指向指標的指標”
    在這里插入圖片描述
  2. 用函式回傳值來傳遞動態記憶體,這種方法更加簡單
    在這里插入圖片描述
  3. 函式 Test5 運行雖然不會出錯,但是函式 GetString2 的設計概念卻是錯誤的, 因為 GetString2 內的“hello world”是常量字串,位于靜態存盤區,它在程式生命期內恒定不變,無論什么時候呼叫 GetString2,它回傳的始終是同一個“只讀”的記憶體塊,
    -在這里插入圖片描述
  4. “堆疊指標”
    在這里插入圖片描述

7.5 free 和 delete 把指標怎么啦

  • 別看 free 和 delete 的名字惡狠狠的(尤其是 delete),它們只是把指標所指的記憶體給釋放掉,但并沒有把指標本身干掉
  • 在這里插入圖片描述

7.6 切記要初始化指標,釋放指標后要置成空指標,

  • 要么將指標設定為 NULL,要么讓它指向合法的記憶體,例如
 char *p = NULL; *
 char *str = (char *) malloc(100); 
  • 不要越界訪問

7.8 有了 malloc/free 為什么還要 new/delete

  • 待定

7.9 記憶體耗盡怎么辦?

  • 如果在申請動態記憶體時找不到足夠大的記憶體塊,malloc 和 new 將回傳 NULL 指標,宣告記憶體申請失敗,通常有三種方式處理“記憶體耗盡”問題,
    (1)判斷指標是否為 NULL,如果是則馬上用 return 陳述句終止本函式,例如:
void Func(void) 
{ 
A *a = new A; 
if(a == NULL) 
{ 
 return; 
 }} 

(2)判斷指標是否為 NULL,如果是則馬上用 exit(1)終止整個程式的運行,例如:

void Func(void) 
{ 
A *a = new A; *
if(a == NULL) 
{ 
 cout << “Memory Exhausted” << endl; 
 exit(1); 
}}

(3)為 new 和 malloc 設定例外處理函式,例如 Visual C++可以用_set_new_hander 函_數為 new 設定用戶自己定義的例外處理函式,也可以讓 malloc 享用與 new 相同的例外處理函式,詳細內容請參考 C++使用手冊

  • 上述(1)(2)方式使用最普遍,如果一個函式內有多處需要申請動態記憶體,那么方式(1)就顯得力不從心(釋放記憶體很麻煩),應該用方式(2)來處理,很多人不忍心用 exit(1),問:“不撰寫出錯處理程式,讓作業系統自己解決行不行?”

  • 不行,如果發生“記憶體耗盡”這樣的事情,一般說來應用程式已經無藥可救,如果不用 exit(1) 把壞程式殺死,它可能會害死作業系統,道理如同:如果不把歹徒擊斃,歹徒在老死之前會犯下更多的罪.

  • 有一個很重要的現象要告訴大家,對于 32 位以上的應用程式而言,無論怎樣使用malloc 與 new,幾乎不可能導致“記憶體耗盡”,我在 Windows 98 下用 Visual C++撰寫了測驗程式,見示例 7-9,這個程式會無休止地運行下去,根本不會終止,因為 32 位作業系統支持“虛存”,記憶體用完了,自動用硬碟空間頂替,我只聽到硬碟嘎吱嘎吱地響,Window 98 已經累得對鍵盤、滑鼠毫無反應

試圖耗盡作業系統的記憶體

void main(void) 
{ 
 float *p = NULL; 
 while(TRUE) 
 { 
 p = new float[1000000]; 
 cout << “eat memory” << endl; 
 if(p==NULL) 
 exit(1); 
 } 
}

7.10 malloc/free 的使用要點

  • 函式 malloc 的原型如下: void * malloc(size_t size);
    用 malloc 申請一塊長度為 length 的整數型別的記憶體,程式如下:
 int *p = (int *) malloc(sizeof(int) * length);

在這里插入圖片描述
在這里插入圖片描述

7.11 new/delete 的使用要點

待定

7.12 一些心得體會

  • (1)越是怕指標,就越要使用指標,不會正確使用指標,肯定算不上是合格的程式員,
  • (2)必須養成“使用除錯器逐步跟蹤程式”的習慣,只有這樣才能發現問題的本質

第 8 章 C++函式的高級特性

  • 對比于 C 語言的函式,C++增加了多載(overloaded)、行內(inline)、const 和 virtual四種新機制,
  • 其中多載和行內機制既可用于全域函式也可用于類的成員函式,const 與virtual 機制僅用于類的成員函式

8.1 函式多載的概念

  • 多載的起源
  • 自 然語言中,一個詞可以有許多不同的含義,即該詞被多載了,人們可以通過背景關系來判斷該詞到底是哪種含義,“詞的多載”可以使語言更加簡練,例如“吃飯”的含義十分廣泛,人們沒有必要每次非得說清楚具體吃什么不可,別迂腐得象孔已己,說茴香豆的茴字有四種寫法,
  • 在 C++程式中,可以將語意、功能相似的幾個函式用同一個名字表示,即函式多載,這樣便于記憶,提高了函式的易用性,這是 C++語言采用多載機制的一個理由,例如示例 8-1-1 中的函式 EatBeef,EatFish,EatChicken 可以用同一個函式名 Eat 表示,用不同型別的引數加以區別,
  void EatBeef();  可以改為 void Eat(Beef …); 
   void EatFish();  可以改為 void Eat(Fish …); 
   void EatChicken();  可以改為 void Eat(Chicken …); 
  • 多載是如何實作的?

    • 幾個同名的多載函式仍然是不同的函式,它們是如何區分的呢?我們自然想到函式
    • 介面的兩個要素:引數與回傳值,
    • 如果同名函式的引數不同(包括型別、順序不同),那么容易區別出它們是不同的函式,
      在這里插入圖片描述
  • 所以只能靠引數而不能靠回傳值型別的不同來區分多載函式,編譯器根據引數為每個多載函式產生不同的內部識別符號,例如編譯器為示例 8-1-1 中的三個 Eat 函式產生象_eat_beef、_eat_fish、_eat_chicken 之類的內部識別符號(不同的編譯器可能產生不同風格的內部識別符號),

  • 如果 C++程式要呼叫已經被編譯后的 C 函式,該怎么辦?假設某個 C 函式的宣告如下:void foo(int x, int y);

    • 該函式被 C 編譯器編譯后在庫中的名字為_foo,而 C++編譯器則會產生像_foo_int_int之類的名字用來支持函式多載和型別安全連接,由于編譯后的名字不同,C++程式不能直接呼叫 C 函式,
    • C++提供了一個 C 連接交換指定符號 extern“C”來解決這個問題,
      例如:
extern “C” 
{ 
 void foo(int x, int y); 
 …  其它函式 
} 
或者寫成
extern “C” 
{ 
 #include “myheader.h” 
 … 其它 C 頭檔案 
} 
  • 這就告訴 C++編譯譯器,函式 foo 是個 C 連接,應該到庫中找名字_foo 而不是找_foo_int_int,C++編譯器開發商已經對 C 標準庫的頭檔案作了 extern“C”處理,所以我們可以用#include 直接參考這些頭檔案,

  • 注意并不是兩個函式的名字相同就能構成多載,全域函式和類的成員函式同名不算多載,因為函式的作用域不同,例如:

 void Print();   全域函式 
 class A 
 {… 
 void Print(); // 成員函式
}
  • 不論兩個 Print 函式的引數是否不同,如果類的某個成員函式要呼叫全域函式 Print,為了與成員函式 Print 區別,全域函式被呼叫時應加‘::’標志,

    • ::Print(…);// 表示 Print 是全域函式而非成員函式
  • 當心隱式型別轉換導致多載函式產生二義性

  • 示例 8-1-3 中,第一個 output 函式的引數是 int 型別,第二個 output 函式的引數是 float 型別,由于數字本身沒有型別,將數字當作引數時將自動進行型別轉換(稱為隱式型別轉換),

    • 陳述句 output(0.5)將產生編譯錯誤,因為編譯器不知道該將 0.5 轉換成int 還是 float 型別的引數,隱式型別轉換在很多地方可以簡化程式的書寫,但是也可能留下隱患,
    • 示例 8-1-3 隱式型別轉換導致多載函式產生二義性 :
 #include <iostream.h> 
void output( int x); // 函式宣告 
void output( float x); // 函式宣告 
 
void output( int x) 
{ 
 cout << " output int " << x << endl ; 
} 
 
void output( float x) 
{ 
 cout << " output float " << x << endl ; 
} 
 
void main(void) 
{ 
 int x = 1; 
 float y = 1.0; 
 output(x);  output int 1 
 output(y);   output float 1 
 output(1);   output int 1 
 output(0.5); 	error! ambiguous call, 因為自動型別轉換 
output(int(0.5));  output int 0 
 output(float(0.5));   output float 0.5 
}

8.2 成員函式的多載、覆寫與隱藏、

  • 多載與覆寫
    • 成員函式被多載的特征:
      • (1)相同的范圍(在同一個類中);
      • (2)函式名字相同;
      • (3)引數不同;
      • (4)virtual 關鍵字可有可無,
    • 覆寫是指派生類函式覆寫基類函式,特征是:
      • (1)不同的范圍(分別位于派生類與基類);
      • (2)函式名字相同;
      • (3)引數相同;
        • (4)基類函式必須有 virtual 關鍵字,
          其余待定

8.3 引數的預設值

待定

8.4 運算子多載

待定

8.5 函式行內

待定

8.6 一些心得體會

  • C++ 語言中的多載、行內、預設引數、隱式轉換等機制展現了很多優點,但是這些優點的背后都隱藏著一些隱患,正如人們的飲食,少食和暴食都不可取,應當恰到好處,我們要辨證地看待 C++的新機制,應該恰如其分地使用它們,雖然這會使我們編程時多費一些心思,少了一些痛快,但這才是編程的藝術,

第 9 章 類的建構式、解構式與賦值函式

待定

第 10 章 類的繼承與組合

待定

第 11 章 其它編程經驗

使用 const 提高函式的健壯性

  • const 更大的魅力是它可以修飾函式的引數、回傳值,甚至函式的定義體,const 是 constant 的縮寫,“恒定不變”的意思,
  • 被 const 修飾的東西都受到強制保護,可以預防意外的變動,能提高程式的健壯性,所以很多 C++程式設計書籍建議:“Use const whenever you need”,
  • 用 const 修飾函式的引數
    • 輸出引數不能用 const
    • 在這里插入圖片描述
    • const 只能修飾輸入引數:
      • 如果輸入引數采用“指標傳遞”,那么加 const 修飾可以防止意外地改動該指標,起到保護作用,
    • 如果輸入引數采用“值傳遞”,由于函式將自動產生臨時變數用于復制該引數,該輸入引數本來就無需保護,所以不要加 const 修飾, 在這里插入圖片描述
  • 總結:
  • 在這里插入圖片描述

用 const 修飾函式的回傳值

  • 如果給以“指標傳遞”方式的函式回傳值加 const 修飾,那么函式回傳值(即指標)的內容不能被修改,該回傳值只能被賦給加 const 修飾的同型別指標,
  • 例如函式
- const char * GetString(void); *
- 如下陳述句將出現編譯錯誤: 
	-  char *str = GetString(); *
- 正確的用法是 
	-  const char *str = GetString();*
  • 如果函式回傳值采用“值傳遞方式”,由于函式會把回傳值復制到外部臨時的存盤單元中,加 const 修飾沒有任何價值, 例如不要把函式 int GetInt(void) 寫成 const int GetInt(void),

提高程式的效率

程式的時間效率是指運行速度,空間效率是指程式占用記憶體或者外存的狀況,
全域效率是指站在整個系統的角度上考慮的效率,區域效率是指站在模塊或函式角度上考慮的效率,

  • 【規則 11-2-1】不要一味地追求程式的效率,應當在滿足正確性、可靠性、健壯性、可讀性等質量因素的前提下,設法提高程式的效率,
  • 【規則 11-2-2】以提高程式的全域效率為主,提高區域效率為輔,
  • 【規則 11-2-3】在優化程式的效率時,應當先找出限制效率的“瓶頸”,不要在無關緊要之處優化,
  • 【規則 11-2-4】先優化資料結構和演算法,再優化執行代碼,
  • 【規則 11-2-5】有時候時間效率和空間效率可能對立,此時應當分析那個更重要,作出適當的折衷,例如多花費一些記憶體來提高性能,
  • 【規則 11-2-6】不要追求緊湊的代碼,因為緊湊的代碼并不能產生高效的機器碼,

一些有益的建議

  • 當心那些視覺上不易分辨的運算子發生書寫錯誤,我們經常會把“==”誤寫成“=”,象“||”、“&&”、“<=”、“>=”這類符號也很容易發生“丟 1”失誤,然而編譯器卻不一定能自動指出這類錯誤,

  • 變數(指標、陣列)被創建之后應當及時把它們初始化,以防止把未被初始化的變數當成右值使用,

  • 當心變數的初值、預設值錯誤,或者精度不夠

  • 當心資料型別轉換發生錯誤,盡量使用顯式的資料型別轉換(讓人們知道發生了什么事),避免讓編譯器輕悄悄地進行隱式的資料型別轉換,

  • 當心變數發生上溢或下溢,陣列的下標越界,

  • 當心忘記撰寫錯誤處理程式,當心錯誤處理程式本身有誤,

  • 當心檔案 I/O 有錯誤,

  • 避免撰寫技巧性很高代碼,

  • 不要設計面面俱到、非常靈活的資料結構,

  • 盡量使用標準庫函式,不要“發明”已經存在的庫函式,

  • 盡量不要使用與具體硬體或軟體環境關系密切的變數,

  • 把編譯器的選擇項設定為最嚴格狀態,

  • 如果可能的話,使用 PC-Lint、LogiScope 等工具進行代碼審查,

GG.本文結尾語:

略顯倉促:
如有和原文較大差異的地方、或不完善的地方、或不明白的地方,可以百度、或者評論區柳巖…!
三聯,三聯,三聯…

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/279633.html

標籤:其他

上一篇:【C語言基礎學習筆記】三、函式(2)--- 函式遞回詳解

下一篇:【CentOS后遺癥】剛畢業的運維小姐姐 Linux用不了!你的也用不了了~

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more