非 const 變量默認爲 extern。要使 const 變量能夠在其他的文件中訪問,必須地指定它爲 extern。
extern int ival = 10; // initializer, so it's a definition
double fica_rate; // no extern, so it's a definition
雖然 ival 聲明爲 extern,但是它有初始化式,代表這條語句是一個定義。類似地,fica_rate 的聲明雖然沒有初始化式,
但也是一個定義,因爲沒有關鍵字 extern。同一個程序中有兩個以上文件含有上述任一個定義都會導致多重定義鏈接錯誤。
引用就是對象的另一個名字。
引用必須用與該引用同類型的對象初始化:
int ival = 1024;
int &refVal = ival; // ok: refVal refers to ival
int &refVal2; // error: a reference must be initialized
int &refVal3 = 10; // error: initializer must be an object
typedef 可以用來定義類型的同義詞:
typedef double wages; // wages is a synonym for double
typedef int exam_score; // exam_score is a synonym for int
typedef wages salary; // indirect synonym for double
typedef 通常被用於以下三種目的:
1.爲了隱藏特定類型的實現,強調使用類型的目的。2.簡化複雜的類型定義,使其更易理解。
3.允許一種類型用於多個目的,同時使得每次使用該類型的目的明確。
枚舉
每個 enum 都定義了一種新的類型。和其他類型一樣,可以定義和初始化 Points 類型的對象,也可以以不同的方式使用這些對象。
枚舉類型的對象的初始化或賦值,只能通過其枚舉成員或同一枚舉類型的其他對象來進行:
Points pt3d = point3d; // ok: point3d is a Points enumerator
Points pt2w = 3; // error: pt2w initialized with int
pt2w = polygon; // error: polygon is not a Points enumerator
pt2w = pt3d; // ok: both are objects of Points enum type
注意把 3 賦給 Points 對象是非法的,即使 3 與一個 Points 枚舉成員相關聯。
class 和 struct區別
用 class 和 struct 關鍵字定義類的唯一差別在於默認訪問級別:默認情況下,struct 的成員爲 public,而 class 的成員爲 private
避免多重包含(頭文件)
在編寫頭文件之前,我們需要引入一些額外的預處理器設施。預處理器允許我們自定義變量。
預處理器變量 的名字在程序中必須是唯一的。任何與預處理器變量相匹配的名字的使用都關聯到該預處理器變量。
爲了避免名字衝突,預處理器變量經常用全大寫字母表示。
預處理器變量有兩種狀態:已定義或未定義。定義預處理器變量和檢測其狀態所用的預處理器指示不同。
#define 指示接受一個名字並定義該名字爲預處理器變量。#ifndef 指示檢測指定的預處理器變量是否未定義。
如果預處理器變量未定義,那麼跟在其後的所有指示都被處理,直到出現 #endif。
可以使用這些設施來預防多次包含同一頭文件:
#ifndef SALESITEM_H
#define SALESITEM_H
// Definition of Sales_itemclass and related functions goes here
#endif
條件指示
#ifndef SALESITEM_H
測試 SALESITEM_H 預處理器變量是否未定義。如果 SALESITEM_H 未定義,那麼 #ifndef 測試成功,
跟在 #ifndef 後面的所有行都被執行,直到發現 #endif。相反,如果 SALESITEM_H 已定義,
那麼 #ifndef 指示測試爲假,該指示和 #endif 指示間的代碼都被忽略。
爲了保證頭文件在給定的源文件中只處理過一次,我們首先檢測 #ifndef。第一次處理頭文件時,測試會成功,
因爲 SALESITEM_H 還未定義。下一條語句定義了 SALESITEM_H。那樣的話,如果我們編譯的文件恰好又一次包含了該頭文件。
#ifndef 指示會發現 SALESITEM_H 已經定義,並且忽略該頭文件的剩餘部分。
#include 指示接受以下兩種形式:
#include <standard_header> #include "my_file.h"
string類的用法
警告:標準庫 string 類型和字符串字面值 因爲歷史原因以及爲了與 C 語言兼容,字符串字面值與標準庫 string 類型不是同一種類型。這一點很容易引起混亂,編程時一定要注意區分字符串字面值和 string 數據類型的使用,這很重要。
Table 3.2. string Operations
s.empty() | Returns true if s is empty; otherwise returns false 如果 s 爲空串,則返回 true,否則返回 false。 |
s.size() | Returns number of characters in s 返回 s 中字符的個數 |
s[n] | Returns the character at position n in s; positions start at 0. 返回 s 中位置爲 n 的字符,位置從 0 開始計數 |
s1 + s2 | Returns a string equal to the concatenation of s1 and s2 把 s1 和s2 連接成一個新字符串,返回新生成的字符串 |
s1 = s2 | Replaces characters in s1 by a copy of s2 把 s1 內容替換爲 s2 的副本 |
v1 == v2 | Returns true if v1 and v2 are equal; false otherwise 比較 v1 與 v2的內容,相等則返回 true,否則返回 false |
!=, <, <=, >, and >= | Have their normal meanings 保持這些操作符慣有的含義 |
任何存儲 string 的 size 操作結果的變量必須爲 string::size_type 類型。特別重要的是,還要把 size 的返回值賦給一個 int 變量。 |
Table 3.3. cctype Functions
isalnum(c) |
True if c is a letter or a digit. 如果 c 是字母或數字,則爲 True。 |
isalpha(c) |
true if c is a letter. 如果 c 是字母,則爲 true。 |
iscntrl(c) |
true if c is a control character. 如果 c 是控制字符,則爲 true |
isdigit(c) |
true if c is a digit. 如果 c 是數字,則爲 true。 |
isgraph(c) |
true if c is not a space but is printable. 如果 c 不是空格,但可打印,則爲 true。 |
islower(c) |
true if c is a lowercase letter. 如果 c 是小寫字母,則爲 true。 |
isprint(c) |
True if c is a printable character. 如果 c 是可打印的字符,則爲 true。 |
ispunct(c) |
True if c is a punctuation character. 如果 c 是標點符號,則 true。 |
isspace(c) |
true if c is whitespace. 如果 c 是空白字符,則爲 true。 |
isupper(c) |
True if c is an uppercase letter. 如果 c 是大寫字母,則 true。 |
isxdigit(c) |
true if c is a hexadecimal digit. 如果是 c 十六進制數,則爲 true。 |
tolower(c) |
If c is an uppercase letter, returns its lowercase equivalent; otherwise returnsc unchanged. 如果 c 大寫字母,返回其小寫字母形式,否則直接返回 c。 |
toupper(c) |
If c is a lowercase letter, returns its uppercase equivalent; otherwise returnsc unchanged. 如果 c 是小寫字母,則返回其大寫字母形式,否則直接返回 c。 |
// an iterator that cannot write elements
vector<int>::const_iterator
// an iterator whose value cannot change
const vector<int>::iterator
迭代器是個所謂的智能指針
3.4.1. 迭代器的算術操作
iter + niter - n
可以對迭代器對象加上或減去一個整形值。這樣做將產生一個新的迭代器,其位置在 iter 所指元素之前(加)或之後(減) n 個元素的位置。加或減之後的結果必須指向 iter 所指 vector 中的某個元素,或者是 vector 末端的後一個元素。加上或減去的值的類型應該是 vector 的 size_type 或 difference_type 類型(參考下面的解釋)。
iter1 - iter2
該表達式用來計算兩個迭代器對象的距離,該距離是名爲 difference_type 的 signed 類型 size_type 的值,這裏的 difference_type 是 signed 類型,因爲減法運算可能產生負數的結果。該類型可以保證足夠大以存儲任何兩個迭代器對象間的距離。iter1 與 iter2 兩者必須都指向同一 vector 中的元素,或者指向 vector 末端之後的下一個元素。
實例:
vector<int>::iterator mid = (vi.begin() + vi.end()) / 2; × ←沒有定義兩個迭代器相加
string* ps1, ps2; // ps1 is a pointer to string, ps2 is a string
defines ps1 as a pointer, but ps2 is a plain string. If we want to define two pointers in a single definition, we must repeat the * on each identifier:
實際上只把 ps1 定義爲指針,而 ps2 並非指針,只是一個普通的 string 對象而已。如果需要在一個聲明語句中定義兩個指針,必須在每個變量標識符前再加符號 * 聲明:
string* ps1, *ps2; // both ps1 and ps2 are pointers to string
初始化動態分配的數組
string *psa = new string[10]; // array of 10 empty strings int *pia = new int[10]; // array of 10 uninitialized ints這兩個 new 表達式都分配了含有 10 個對象的數組。其中第一個數組是 string 類型,分配了保存對象的內存空間後,將調用 string 類型的默認構造函數依次初始化數組中的每個元素。第二個數組則具有內置類型的元素,分配了存儲 10 個 int 對象的內存空間,但這些元素沒有初始化。
也可使用跟在數組長度後面的一對空圓括號,對數組元素做值初始化(第 3.3.1 節):
int *pia2 = new int[10] (); // array of 10 uninitialized ints
圓括號要求編譯器對數組做值初始化,在本例中即把數組元素都設置爲0。
引用形參
爲了使 swap 函數以期望的方式工作,交換實參的值,需要將形參定義爲引用類型:
// ok: swap acts on references to its arguments
void swap(int &v1, int &v2)
{
int tmp = v2;
v2 = v1;
v1 = tmp;
}
與所有引用一樣,引用形參直接關聯到其所綁定的聖賢,而並非這些對象的副本。定義引用時,必須用與該引用綁定的對象初始化該引用。引用形參完全以相同的方式工作。每次調用函數,引用形參被創建並與相應實參關聯。此時,當調用 swap
swap(i, j);
形參 v1 只是對象 i 的另一個名字,而 v2 則是對象 j 的另一個名字。對 v1 的任何修改實際上也是對 i 的修改。同樣地,v2 上的任何修改實際上也是對 j 的修改。重新編譯使用 swap 的這個修訂版本的 main 函數後,可以看到輸出結果是正確的:
Before swap(): i: 10 j: 20
After swap(): i: 20 j: 10
static 局部對象
一個變量如果位於函數的作用域內,但生命期跨越了這個函數的多次調用,這種變量往往很有用。則應該將這樣的對象定義爲 static(靜態的)。static 局部對象確保不遲於在程序執行流程第一次經過該對象的定義語句時進行初始化。這種對象一旦被創建,在程序結束前都不會撤銷。當定義靜態局部對象的函數結束時,靜態局部對象不會撤銷。在該函數被多次調用的過程中,靜態局部對象會持續存在並保持它的值。
inline 函數
1.編譯時展開
2.聲明和實現最好都放在頭文件裏,方便調用的函數展開。
7.9. 指向函數的指針
函數指針是指指向函數而非指向對象的指針。像其他指針一樣,函數指針也指向某個特定的類型。函數類型由其返回類型以及形參表確定,而與函數名無關:// pf points to function returning bool that takes two const string references
bool (*pf)(const string &, const string &);
這個語句將 pf 聲明爲指向函數的指針,它所指向的函數帶有兩個 const string& 類型的形參和 bool 類型的返回值。
*pf 兩側的圓括號是必需的:
// declares a function named pf that returns a bool*
bool *pf(const string &, const string &);