第1章 更好的C
- C++是型別安全語言
- 所有函數在第一次使用之前必須聲明或定義
- 引用參數直接支援引用調用語義
- 模板允許建立通用函數
- 內嵌函式將類似於函數的宏的高效與實際函數的安全性相結合
- 聲明可以出現在函數可以出現的任意位置
第2章 指標
- C和C++僅僅與那些使用它們的人一樣危險
- 指標是地址
- 可以將任何一個指標賦值成void*
- 注意區分一個const指標和一個指向const的指標
- p+n == (char*)p+n*sizeof(*p)
- *(a+i) == a[i]
- 除非在sizeof和&的上下文中,否則一個數組名即是指向它第一個元素的指標
- 沒有多維陣列,只有數組的數組
- 僅是指標的存在並不要求它所引用的類型的實現的有效性(這是一個不完全類型)
如果理解了這些概念,你就正在逐漸成為一名可信賴的C++程式員。
第3章 前置處理器
- 前置處理器不能理解語言
- 頭不一定是檔案?(不理解)
- 徹底的記住帶括弧的宏
- 寧可用內嵌函式而不用類函數的宏(除了字串化和標記粘貼)
- 寧可用常值而不要用類對象的宏
- 用assert宏來捕捉不應該發生的概念錯誤
- 有條件地用特殊的宏來編譯標頭檔(來避免迴圈包含)
- C和C++支援三字元組合以適應國際鍵盤,C++支援更多可讀雙字元和其他保留字
第4章 C標準庫之一:面向合格的程式員
- <ctype.h>字元處理,isupper(c),tolower(),…
- <stdio.h>輸入輸出,包括格式化函數、字元I/O函數和檔案函數
- <stdlib.h>複雜的工具,含有div函數返回的結構div_t{quot商,rem餘數},長整數是ldiv_t
- <string.h>文本處理,複製、連結、比較和尋找
第5章 C標準庫之二:面向熟練的程式員
- <assert.h>支援有保護的程式的斷言
- <limits.h>整數運算的系統參數
- <stddef.h>通用類型和常量 ,包含寬字元類型wchar_t
- <time.h>時間處理
第6章 C標準庫之二:面向優秀的程式員
- <errno.h>錯誤偵測
- <float.h>實數運算的系統參數
- <locale.h>文化自適應
- <math.h>數學函數
- <setjmp.h>非局部分支
- <signal.h>中斷處理
- <stdarg.h>可變長度參數表
第7章 抽象
- 複雜性是生活中不可避免的事實
- 抽象是處理複雜性的工具
- 定義明確的抽象資料類型可把介面從實現中分離出來
- C++類支援資料抽象
- 成員存取控制限定詞(private和protected)支援封裝
- 具體的類型應該有定義明確的建構函式、拷貝建構函式、賦值運算子合一個解構函式
- 運算子多載可以使具體的類型的行為類似於內建類型
- 模板支援類型抽象
- 虛函數支援函數抽象
Date類
//date.h#ifndef DATE_H#define DATE_H#include <iostream>using std::ostream;struct DateRep;class Date{public: Date(int, int, int); ~Date(); char* format(char*) const; int compare(const Date&) const; friend ostream& operator<<(ostream&, const Date&);private: struct DateRep* drep; static const char* month_text[13];};#endif
//date.cpp#include <stdio.h>#include "date.h"struct DateRep{ DateRep(int, int, int);private: friend struct Date; friend ostream& operator<<(ostream&, const Date&); int month; int day; int year;};const char* Date::month_text[13] = {"Bad month", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; DateRep::DateRep(int m, int d, int y):month(m),day(d),year(y){}Date::Date(int m, int d, int y){ drep = new DateRep(m,d,y);}Date::~Date(){ delete drep;}char* Date::format(char* buf) const{ sprintf(buf, "%s %d, %d", month_text[drep->month], drep->day,drep->year); return buf;}int Date::compare(const Date& dp2) const{ int result = drep->year - dp2.drep->year; if (result == 0) result = drep->month - dp2.drep->month; if (result == 0) result = drep->day - dp2.drep->day; return result;}ostream& operator<<(ostream& os, const Date& d){ os << Date::month_text[d.drep->month] << ' ' << d.drep->day << ", " << d.drep->year; return os;}
//tdate.cpp#include <iostream>#include "date.h"using namespace std;int main(){ Date d1(10,1,1951),d2(3,7,1995); cout << "d1=" << d1 << endl; cout << "d2=" << d2 << endl; return 0;}
第9章 位操作
- 位操作支援系統編程而且能降低記憶體需要
- 按位元運算符允許訪問整數位的子集(但是要注意運算子的優先權)
- bitset類模板支援有效固定大小的位設定作業
- vector<bool>模板規範支援動態大小的位字串
第10章 類型轉換盒強制類型轉換
- C和C++提供隱式類型轉換以簡化混合模式運算
- 在運算中整數類型可以根據需要而升級
- 浮點運算由標準的類型轉換來“平衡”
- 函數原型協助編譯器提供隱式類型轉換
- 有時候不希望使用者定義型別的隱式類型轉換
- 當想屏蔽隱式類型轉換時,可以顯示地聲明帶有單個參數的建構函式
- 強制類型轉換使得類型轉換變得清楚
- 盡量使用函數風格的強制類型轉換,而不是C風格的強制類型轉換
- 當適用時使用新風格的強制類型轉換(即除了使用建構函式調用時,總是使用)
- 當適用時盡量使用const
- 首選語義常量而不是位邏輯常量
- 可變的儲存類使得在大多數情況下去掉const變得沒有必要
- RTTI允許安全的向下強制類型轉換
第11章 可見度
- 在C++檔案裡:extern “C” f();告訴編譯器根據C的規則產生串連名
- 標識符可以有外部串連(在多重翻譯單元內可見)、內部串連(只在單獨的翻譯單元中可見)和沒有串連
- 有外部串連的對象必須在一個被命名的名字空間中定義(包括被秘密命名的全域名字空間)
第12章 控制結構
- 用斷言或注釋顯示不變的條件
- break退出密封關閉的迴圈或分支
- continue跳到最近關閉的迴圈的下一個迭代
- 用setjmp/longjmp機制在異常條件下交替地返回
- 用訊號處理常式捕獲同步訊號和非同步訊號
第13章 異常
- 錯誤處理方法
- 忽略錯誤
- 檢查傳回碼
- 用非局部跳轉來使執行過程改變方向
- 使用異常
- 異常處理是一個運行期機制
- C++異常處理是圍繞中斷模式設計的
- 標準異常
- exception
- logic_error
- domain_error
- invalid_argument
- length_error
- out_of_range
- runtime_error
- range_error
- overflow_error
- underflow_error
- bad_alloc
- bad_cast
- bad_exception
- bad_typeid
- ”資源分派是初始化“原則
- 向語言中加入異常的目的之一是為了彌補建構函式無傳回值這一不足之處
- 為不能就地處理的錯誤拋出異常,包括無效的參數
- 不要在有建構函式和解構函式的對象前使用setjmp和longjmp。而是要用異常。當一次被拋出時,堆棧中所有的自動對象將被自動銷毀
- 異常允許獨立的程式組件關於錯誤條件進行通訊
- 通過與拋出的對象的類型相匹配來選擇處理常式
- 根據被處理的類型從最特殊到最普通的順序排列相鄰的處理常式
- 如果用標準庫,準備好在標準繼承結構裡捕獲異常(至少是exception)
- 在自動對象中封裝資源分派以確保在需要的時候調用解構函式
- 在auto_ptr中封裝new運算式的結果以確保解構函式可以被合適地調用
- 可以對文檔使用異常規範並加強函數所拋出的異常類型
- 斷言只用來防止你(快速的開發人員)的錯誤
- 為每一個組件定義一個從exception類(或runtime_error或logic_error,如果是和的話)派生出來的異常類
第14章 物件導向編程
- 三要素:封裝,繼承,多態
- 共有繼承表示基類和衍生類別的“is-a”關係
- 指向基類的指標還可以指向衍生類別對象,衍生類別對象總是可以代替基類對象
- 多態是指有關聯的實現共用一個單獨的介面,實現可以根據需要隨著所包含的物件類型變化而自動化。C++通過虛函數支援多態
- 虛函數通過函數指標表來類比動態綁定
第15章 演算法
- 演算法種類:通用序列演算法,有關排序的演算法,數值演算法。其中前兩個演算法包含在<algorithm>中,數值演算法包含在<numeric>中
- 演算法是電腦科學的“要素”
- C++標準庫中的演算法是函數模板,他們中的大多數都是處理由[begin,end)所限定範圍的一系列對象
- 函數對象是一個定義運算子的類的執行個體
- 可改寫的函數對象含有對它的參數和傳回值類型的定義
- 判斷是一個返回bool類型的函數或者函數對象
- 函數對象適配器是一個函數模板,它把一個或者多個函數對象以及其他可選的數值作為參數,並且返回一個新的函數對象
- 通過把函數對象和函數對象適配器配合,可以自訂演算法斷言,通常沒必要自己編寫函數
第16章 容器和迭代器
- 標準容器可以支援任何具體類型的對象,包括其他的容器。具體的類型是有值語義的類型,意味著它們可以被複製、賦值和比較相等或者不相等
- 標準容器包括:基本序列容器、容器適配器和關聯容器
- 基本序列容器包括:向量、雙端對列和表,它們之間的不同主要在於在哪允許元素的有效插入和刪除,以及它們支援何種迭代器
- 迭代器是指標的一般化,迭代器是用通用方式串連演算法和容器的粘結劑
- 迭代器分五種:輸入迭代器,輸出迭代器,前向迭代器,雙向迭代器和隨機訪問迭代器
- 不支援演算法的迭代器的容器經常要求定義它們自己的板式(如,list<T>sort())
- 容器適配器包括:棧,隊列和優先權隊列。容器適配器用更嚴格的介面來封裝基本序列以實現更進階的資料抽象
- 關聯容器是包括:集合,多重集合,圖和多重圖。關聯容器集合和圖僅儲存唯一的關鍵詞
- 特殊用途的容器bitset和valarray不支援其他標準容器所支援的容器——迭代器——演算法主題
第17&&18章 文本處理
- 在互動式應用程式中每次讀入一行,然後在必要的時候解析每一行這是一個很好的習慣
- 標準C++字串類最重要的特徵就是記憶體管理
- 標準C++庫通過模板basic_string支援任何“類字元型”的字串
- 如果標準庫不能滿足檔案處理的需要,試著用POSIX檔案操作。如果還不行,把依賴平台的代碼分隔在獨立的模組中以利於連接埠進程
- 總是考慮用C++來封裝低層級的概念和操作
第19章 時間和日期處理
- 利用<time.h>中的函數,如localtime、strftime和difftime,進行簡單的時間和日期操作
- 確信知道環境的參考日期
第20章 動態記憶體管理
- 區分淺拷貝和深拷貝
- 重載new和delete:
- 標準C++庫定義了12個用於分配和釋放記憶體的函數。當使用new運算子是,它會計算出所需要的位元組數然後按順序調用這些函數中的一個來分配這些位元組。這12個函數是:
- void *operator new(size_t);//標量形式
- void *operator new(size_t,const nothrow_t);
- void *operator delete(void*);
- void *operator delete(void*,const nothrow_t);
- void *operator new[](size_t);//數組形式
- void *operator new[](size_t,const nothrow_t);
- void *operator delete[](void*);
- void *operator delete[](void*,const nothrow_t);
- void *operator new(size_t,void *);//配置new
- void *operator new[](size_t,void *);
- void *operator delete(void*, void*);
- void *operator delete[](void*,void*);
- 配置new
- 當預先不知道需要多少記憶體來儲存一個對象,或者不知道需要多少個對象是,可以使用動態記憶體
- new運算子調用operator new 來為一個動態對象分配記憶體,然後再調用適當的建構函式。delete運算子通過調用對象的解構函式來銷毀這個對象,然後調用operator delete
- 對於數組一定要使用delete[]
- 無論什麼時候,當一個類是指標成員時,都要檢查是否需要深拷貝
- 記住對記憶體操作的失敗將引發bad_alloc異常
- 在給定的地址上使用配置new來構建一個對象
- 為了使用記憶體片段的數量儘可能少,可以考慮將動態對象的同類集合分配到一個專用的記憶體池中,如容器。
- 無論什麼時候,只要有可能就讓標準庫來為你管理記憶體
上述知識點都是摘抄自《C和C++代碼精粹》中文版。