# Terminology(術語)
作者:Scott Meyers
譯者:fatalerror99 (iTePub's Nirvana)
發布:http://blog.csdn.net/fatalerror99/
這是一個所有程序員都應該了解的小型的 C++ vocabulary(詞匯表)。下面的術語都足夠重要,對它們的含義取得完全一致對于我們來說是完全必要的。
declaration(聲明)告訴編譯器關于某物的 name(名字)和 type(類型),但它省略了某些細節。以下這些都是 declaration(聲明):
extern int x; // object declaration
std::size_t numDigits(int number); // function declaration
class Widget; // class declaration
template<typename T> // template declaration
class GraphNode; // (see Item 42 for info on
// the use of "typename")
注意即使是 built-in type(內建類型),我還是更喜歡將整數 x 看作一個 "object",某些人將 "object" 這個名字保留給 user-defined type(用戶定義類型),但我不是他們中的一員。再有就是注意函數 numDigits 的返回類型是 std::size_t,也就是說,namespace(命名空間)std 中的 size_t 類型。這個 namespace(命名空間)是 C++ 標準庫中每一樣東西實際所在的地方。但是,因為 C 標準庫(嚴謹地說,來自于 C89)在 C++ 中也能使用,從 C 繼承來的符號(諸如 size_t)可能存在于全局范圍,或 std 內部,或兩者都有,這依賴于哪一個頭文件被 #include。在本書中,我假設 C++ 頭文件被 #include,這也就是為什么我用 std::size_t 代替 size_t 的原因。當文字討論中涉及到標準庫組件時,我一般不再提及 std,這依賴于你認可類似 size_t,vector,以及 cout 之類的東西都在 std 中,在示例代碼中,我總是包含 std,因為真正的代碼沒有它將無法編譯。
順便說一下,size_t 僅僅是供 C++ 對某物計數時使用的某些 unsigned 類型的 typedef(例如,一個 char\*-based string(基于 char\* 的 string)中字符的個數,一個 STL container(容器)中元素的個數,等等)。它也是 vector,deque,和 string 的 operator[] 函數所持有的類型,這是一個在 Item 3 中定義我們自己的 operator[] 函數時將要遵守的慣例。
每一個函數的 declaration(聲明)都表明了它的 signature(識別特征),也就是它的參數和返回類型。一個函數的 signature(識別特征)與它的類型相同。對于 numDigits 的情況,signature(識別特征)是 std::size_t (int),也就是說,“函數取得一個 int,并返回一個 std::size_t”。官方的 "signature"(識別特征)的 C++ 定義排除了函數的返回類型,但是在本書中,將返回類型考慮為 signature(識別特征)的一部分更加有用。
definition(定義)為編譯器提供在 declaration(聲明)時被省略的細節。對于一個 object(對象),definition(定義)是編譯器為 object(對象)留出內存的地方。對于一個 function(函數)或一個 function template(函數模板),definition(定義)提供代碼本體。對于一個 class(類)或一個 class template(類模板),definition(定義)列出了 class(類)或者 template(模板)的 members(成員):
int x; // object definition
std::size_t numDigits(int number) // function definition.
{ // (This function returns
std::size_t digitsSoFar = 1; // the number of digits
// in its parameter.)
while ((number /= 10) != 0) ++digitsSoFar;
return digitsSoFar;
}
class Widget { // class definition
public:
Widget();
~Widget();
...
};
template<typename T> // template definition
class GraphNode {
public:
GraphNode();
~GraphNode();
...
};
initialization(初始化)是設定一個 object(對象)的第一個值的過程。對于 user-defined types(用戶定義類型)的 objects(對象),initialization(初始化)通過 constructors(構造函數)完成。default constructor(缺省構造函數)就是不需要任何 arguments(引數)就可以調用的那一個。這樣的一個 constructor(構造函數)既可以是沒有 parameters(參數),也可以是每一個 parameter(參數)都有缺省值:
class A {
public:
A(); // default constructor
};
class B {
public:
explicit B(int x = 0, bool b = true); // default constructor; see below
}; // for info on "explicit"
class C {
public:
explicit C(int x); // not a default constructor
};
這里 classes B 和 C 的 constructors(構造函數)都被聲明為 explicit(顯式)。這是為了防止它們被用來執行 implicit type conversions(隱式類型轉換),雖然他們還可以被用于 explicit type conversions(顯示類型轉換):
```
void doSomething(B bObject); // a function taking an object of
// type B
B bObj1; // an object of type B
doSomething(bObj1); // fine, passes a B to doSomething
B bObj2(28); // fine, creates a B from the int 28
// (the bool defaults to true)
doSomething(28); // error! doSomething takes a B,
// not an int, and there is no
// implicit conversion from int to B
doSomething(B(28)); // fine, uses the B constructor to
// explicitly convert (i.e., cast) the
// int to a B for this call. (See
// Item 27 for info on casting.)
```
constructors(構造函數)被聲明為 explicit(顯式)通常比 non-explicit(非顯式)更可取,因為它們可以防止編譯器執行意外的(常常是無意識的)type conversions(類型轉換)。除非我有一個好的理由允許一個 constructor(構造函數)被用于 implicit type conversions(隱式類型轉換),否則我就將它聲明為 explicit(顯式)。我希望你能遵循同樣的方針。
請注意我是如何突出上面的示例代碼中的 cast(強制轉換)的。貫穿本書,我用這樣的突出引導你注意那些應該注意的材料。(我也突出章節號碼,但那僅僅是因為我想讓它好看一些。)
copy constructor(拷貝構造函數)被用來以一個 object(對象)來初始化同類型的另一個 object(對象),copy assignment operator(拷貝賦值運算符)被用來將一個 object(對象)中的值拷貝到同類型的另一個 object(對象)中:
```
class Widget {
public:
Widget(); // default constructor
Widget(const Widget& rhs); // copy constructor
Widget& operator=(const Widget& rhs); // copy assignment operator
...
};
Widget w1; // invoke default constructor
Widget w2(w1); // invoke copy constructor
w1 = w2; // invoke copy
// assignment operator
```
當你看到什么東西看起來像一個 assignment(賦值)的話,要仔細閱讀,因為 "=" 在語法上還可以被用來調用 copy constructor(拷貝構造函數):
```
Widget w3 = w2; // invoke copy constructor!
```
幸運的是,copy constructor(拷貝構造函數)很容易從 copy assignment(拷貝賦值)中區別出來。如果一個新的 object(對象)被定義(就象上面那行代碼中的 w3),一個 constructor(構造函數)必須被調用;它不可能是一個 assignment(賦值)。如果沒有新的 object(對象)被定義(就象上面那行 "w1 = w2" 代碼中),沒有 constructor(構造函數)能被調用,所以它就是一個 assignment(賦值)。
copy constructor(拷貝構造函數)是一個特別重要的函數,因為它定義一個 object(對象)如何 passed by value(通過傳值的方式被傳遞)。例如,考慮這個:
```
bool hasAcceptableQuality(Widget w);
...
Widget aWidget;
if (hasAcceptableQuality(aWidget)) ...
```
參數 w 通過傳值的方式被傳遞給 hasAcceptableQuality,所以在上面的調用中,aWidget 被拷貝給 w。拷貝動作通過 Widget 的 copy constructor(拷貝構造函數)被執行。pass-by-value(通過傳值方式傳遞)意味著 "call the copy constructor"(調用拷貝構造函數)。(然而,通過傳值方式傳遞 user-defined types(用戶定義類型)通常是一個不好的想法,pass-by-reference-to-const(傳引用給 const)通常是更好的選擇。關于細節,參見 Item 20。)
STL 是 Standard Template Library(標準模板庫),作為 C++ 標準庫的一部分,致力于 containers(容器)(例如,vector,list,set,map,等等),iterators(迭代器)(例如,vector<int>::iterator,set<string>::iterator,等等),algorithms(算法)(例如,for_each,find,sort,等等),以及相關機能。相關機能中的很多都通過 function objects(函數對象)——行為表現類似于函數的 objects(對象)——提供。這樣的 objects(對象)來自于重載了 operator() ——函數調用運算符——的 class(類),如果你不熟悉 STL,在讀本書的時候,你應該有一本像樣的參考手冊備查,因為對于我來說 STL 太有用了,以至于不能不利用它。一但你用了一點點,你也會有同樣的感覺。
從 Java 或 C# 那樣的語言來到 C++ 的程序員可能會對 undefined behavior(未定義行為)的概念感到吃驚。因為各種各樣的原因,C++ 中的一些 constructs(結構成分)的行為沒有確切的定義:你不能可靠地預知運行時會發生什么。這里是兩個帶有 undefined behavior(未定義行為)的代碼的例子:
```
int *p = 0; // p is a null pointer
std::cout << *p; // dereferencing a null pointer
// yields undefined behavior
char name[] = "Darla"; // name is an array of size 6 (don't
// forget the trailing null!)
char c = name[10]; // referring to an invalid array index
// yields undefined behavior
```
為了強調 undefined behavior(未定義行為)的結果是不可預言而且可能是令人討厭的,有經驗的 C++ 程序員常常說帶有 undefined behavior(未定義行為)的程序 can(能)毀掉你的辛苦工作的成果。這是真的:一個帶有 undefined behavior(未定義行為)的程序 could(可以)毀掉你的心血。只不過可能性不太大。更可能的是那個程序的表現反復無常,有時會運行正常,有時會徹底完蛋,還有時會產生錯誤的結果。有實力的 C++ 程序員能以最佳狀態避開 undefined behavior(未定義行為)。本書中,我會指出許多你必須要注意它的地方。
另一個可能把從其它語言轉到 C++ 的程序員搞糊涂的術語是 interface(接口)。Java 和 .NET 的語言都將 Interfaces(接口)作為一種語言要素,但是在 C++ 中沒有這種事,但是在 Item 31 討論了如何模擬它。當我使用術語 "interface"(接口)時,一般情況下我說的是一個函數的 signature(識別特征),是一個 class(類)的可訪問元素(例如,一個 class(類)的 "public interface","protected interface",或 "private interface"),或者是對一個 template(模板)的 type parameter(類型參數)來說必須合法的 expressions(表達式)(參見 Item 41)。也就是說,我是作為一個相當普遍的設計概念來談論 interface(接口)的。
client(客戶)是使用你寫的代碼(一般是 interfaces(接口))的某人或某物。例如,一個函數的 clients(客戶)就是它的使用者:調用這個函數(或持有它的地址)的代碼的片段以及寫出和維護這樣的代碼的人。class(類)或者 template(模板)的 clients(客戶)是使用這個 class(類)或 template(模板)的軟件的部件,以及寫出和維護那些代碼的程序員。在討論 clients(客戶)的時候,我一般指向程序員,因為程序員會被困擾和誤導,或者因為不好的 interfaces(接口)而煩惱。但他們寫的代碼卻不會。
你也許不習慣于為 clients(客戶)著想,但是我會用大量的時間試圖說服你:你應該盡你所能使他們的生活更輕松。記住,你也是一個其他人開發的軟件的 client(客戶)。難道你不希望那些人為你把事情弄得輕松些嗎?除此之外,你幾乎肯定會在某個時候發現你自己處在了你自己的 client(客戶)的位置上(也就是說,使用你寫的代碼),而這個時候,你會為你在開發你的 interfaces(接口)時在頭腦中保持了對 client(客戶)的關心而感到高興。
在本書中,我常常掩蓋 functions(函數)和 function templates(函數模板)之間以及 classes(類)和 class templates(類模板)之間的區別。那是因為對其中一個確定的事對另一個常常也可以確定。如果不是這樣,我會區別對待 classes(類),functions(函數),以及由 classes(類)和 functions(函數)產生的 templates(模板)。
在代碼注釋中提到 constructor(構造函數)和 destructors(析構函數)時,我有時使用縮寫形式 ctor 和 dtor。
Naming Conventions(命名慣例)
我試圖為 objects(對象),classes(類),functions(函數),templates(模板)等選擇意味深長的名字,但是在我的某些名字后面的含義可能不會立即顯現出來。例如,我特別喜歡的兩個 parameter names(參數名字)是 lhs 和 rhs。它們分別代表 "left-hand side" 和 "right-hand side"。我經常用它們作為實現 binary operators(二元運算符)的函數(例如,operator== 和 operator\*)的 parameter names(參數名字)。例如,如果 a 和 b 是代表有理數的 objects(對象),而且如果 Rational objects(對象)能通過一個 non-member(非成員)的 operator\* 函數相乘(Item 24 中解釋的很可能就是這種情況),表達式
```
a * b
```
與函數調用
```
operator*(a,b)
```
就是等價的。
在 Item 24 中,我這樣聲明 operator\*:
```
const Rational operator*(const Rational& lhs, const Rational& rhs);
```
你可以看到,left-hand operand(左手操作數)a 在函數內部以 lhs 的面目出現,而 right-hand operand(右手操作數)b 以 rhs 的面目出現。
對于 member functions(成員函數),left-hand argument(左手參數)表現為 this pointer(this 指針),所以有時候我單獨使用 parameter name(參數名字) rhs。你可能已經在第 5 頁中某些 Widget 的 member functions(成員函數)的 declarations(聲明)(本文介紹 copy constructor(拷貝構造函數)的那一段中的例子——譯者注)中注意到了這一點。這一點提醒了我。我經常在示例中使用 Widget class(類)。"Widget" 并不意味著什么東西。它僅僅是在我需要一個示例類的名字的時候不時地使用一下的名字。它和 GUI 工具包中的 widgets 沒有任何關系。
我經常遵循這個規則為 pointers(指針)命名:一個指向 type(類型)T 的 object(對象)的 pointer(指針)被稱為 pt,"pointer to T"。以下是例子:
```
Widget *pw; // pw = ptr to Widget
class Airplane;
Airplane *pa; // pa = ptr to Airplane
class GameCharacter;
GameCharacter *pgc; // pgc = ptr to GameCharacter
```
我對 references(引用)使用類似的慣例:rw 可以認為是一個 reference to a
Widget(引向一個 Widget 的引用),而 ra 是一個 reference to an Airplane(引向一個 Airplane 的引用)。
在討論 member functions(成員函數)的時候我偶爾會使用名字 mf。
Threading Considerations(對線程的考慮)
作為一種語言,C++ 沒有 threads(線程)的概念——實際上,是沒有任何一種 concurrency(并發)的概念。對于 C++ 標準庫也是同樣如此。就 C++ 涉及的范圍而言,multithreaded programs(多線程編程)并不存在。
而且至今它們依然如此。我致力于讓此書基于標準的,可移植的 C++,但我也不能對 thread safety(線程安全)已成為很多程序員所面臨的一個問題的事實視而不見。我對付這個標準 C++ 和現實之間的裂痕的方法就是指出某個 C++ constructs(結構成分)以我的分析很可能在 threaded environment(線程環境)中引起問題的地方。這樣不但不會使本書成為一本 multithreaded programming with C++(用 C++ 進行多線程編程)的書。反而,它更會使本書在相當程度上成為這樣一本 C++ 編程的書:將自己在很大程度上限制于 single-threaded(單線程)思路,承認 multithreading(多線程)的存在,并試圖指出有線程意識的程序員需要特別留心評估我提供的建議的地方。
如果你不熟悉 multithreading(多線程)或者不必為此擔心,你可以忽略我關于線程的討論。如果你正在編寫一個多線程的應用或庫,無論如何,請記住我的評注和并將它作為你使用 C++ 時需要致力去解決的問題的起點。
TR1 和 Boost
你會發現提及 TR1 和 Boost 的地方遍及全書。它們每一個都有一個專門的 Item 在某些細節上進行描述(Item 54 是 TR1,Item 55 是 Boost),但是,不幸的是,這些 Item 在全書的最后。(他們在那里是因為那樣更好一些,我確實試過很多其它的地方。)如果你愿意,你現在就可以翻開并閱讀那些 Item,但是如果你更喜歡從本書的起始處而不是結尾處開始,以下摘要會對你有所幫助:
* TR1 ("Technical Report 1") 是被加入 C++ 標準庫的新機能的 specification(規格說明書)。這些機能以新的 class(類)和 function templates(函數模板)的形式提供了諸如 hash tables(哈希表),reference-counting smart pointers(引用計數智能指針),regular expressions(正則表達式),等等。所有的 TR1 組件都位于嵌套在 namespace std 內部的 namespace tr1 內。
* Boost 是一個組織和一個網站 (http://boost.org) 提供的可移植的,經過同行評審的,開源的 C++ 庫。大多數 TR1 機能都基于 Boost 的工作,而且直到編譯器廠商在他們的 C++ 庫發行版中包含 TR1 之前,Boost 網站很可能會保持開發者尋找 TR1 實現的第一站的地位。Boost 提供的東西比用于 TR1 的更多,無論如何,在很多情況下,它還是值得去了解一下的。
- Preface(前言)
- Introduction(導言)
- Terminology(術語)
- Item 1: 將 C++ 視為 federation of languages(語言聯合體)
- Item 2: 用 consts, enums 和 inlines 取代 #defines
- Item 3: 只要可能就用 const
- Item 4: 確保 objects(對象)在使用前被初始化
- Item 5: 了解 C++ 為你偷偷地加上和調用了什么函數
- Item 6: 如果你不想使用 compiler-generated functions(編譯器生成函數),就明確拒絕
- Item 7: 在 polymorphic base classes(多態基類)中將 destructors(析構函數)聲明為 virtual(虛擬)
- Item 8: 防止因為 exceptions(異常)而離開 destructors(析構函數)
- Item 9: 絕不要在 construction(構造)或 destruction(析構)期間調用 virtual functions(虛擬函數)
- Item 10: 讓 assignment operators(賦值運算符)返回一個 reference to *this(引向 *this 的引用)
- Item 11: 在 operator= 中處理 assignment to self(自賦值)
- Item 12: 拷貝一個對象的所有組成部分
- Item 13: 使用對象管理資源
- Item 14: 謹慎考慮資源管理類的拷貝行為
- Item 15: 在資源管理類中準備訪問裸資源(raw resources)
- Item 16: 使用相同形式的 new 和 delete
- Item 17: 在一個獨立的語句中將 new 出來的對象存入智能指針
- Item 18: 使接口易于正確使用,而難以錯誤使用
- Item 19: 視類設計為類型設計
- Item 20: 用 pass-by-reference-to-const(傳引用給 const)取代 pass-by-value(傳值)
- Item 21: 當你必須返回一個對象時不要試圖返回一個引用
- Item 22: 將數據成員聲明為 private
- Item 23: 用非成員非友元函數取代成員函數
- Item 24: 當類型轉換應該用于所有參數時,聲明為非成員函數
- Item 25: 考慮支持不拋異常的 swap
- Item 26: 只要有可能就推遲變量定義
- Item 27: 將強制轉型減到最少
- Item 28: 避免返回對象內部構件的“句柄”
- Item 29: 爭取異常安全(exception-safe)的代碼
- Item 30: 理解 inline 化的介入和排除
- Item 31: 最小化文件之間的編譯依賴
- Item 32: 確保 public inheritance 模擬 "is-a"
- Item 33: 避免覆蓋(hiding)“通過繼承得到的名字”
- Item 34: 區分 inheritance of interface(接口繼承)和 inheritance of implementation(實現繼承)
- Item 35: 考慮可選的 virtual functions(虛擬函數)的替代方法
- Item 36: 絕不要重定義一個 inherited non-virtual function(通過繼承得到的非虛擬函數)
- Item 37: 絕不要重定義一個函數的 inherited default parameter value(通過繼承得到的缺省參數值)
- Item 38: 通過 composition(復合)模擬 "has-a"(有一個)或 "is-implemented-in-terms-of"(是根據……實現的)
- Item 39: 謹慎使用 private inheritance(私有繼承)
- Item 40: 謹慎使用 multiple inheritance(多繼承)
- Item 41: 理解 implicit interfaces(隱式接口)和 compile-time polymorphism(編譯期多態)
- Item 42: 理解 typename 的兩個含義
- Item 43: 了解如何訪問 templatized base classes(模板化基類)中的名字
- Item 44: 從 templates(模板)中分離出 parameter-independent(參數無關)的代碼
- Item 45: 用 member function templates(成員函數模板) 接受 "all compatible types"(“所有兼容類型”)
- Item 46: 需要 type conversions(類型轉換)時在 templates(模板)內定義 non-member functions(非成員函數)
- Item 47: 為類型信息使用 traits classes(特征類)
- Item 48: 感受 template metaprogramming(模板元編程)
- Item 49: 了解 new-handler 的行為
- Item 50: 領會何時替換 new 和 delete 才有意義
- Item 51: 編寫 new 和 delete 時要遵守慣例
- Item 52: 如果編寫了 placement new,就要編寫 placement delete
- 附錄 A. 超越 Effective C++
- 附錄 B. 第二和第三版之間的 Item 映射