7.2 Argument Passing 參數傳遞
Reference Parameters引用形參
引用形參并沒有對對象進行copy,而是指向它綁定的對象。
這段代碼是引用形參的經典用法:
void swap(int &v1, int &v2)
{
int tmp = v2;
v2 = v1; // assigns new value to local copy of the argument
v1 = tmp;
}
|
實際上,這個例子在C代碼中也最常見,但是不是用引用形參,而是用指針。二者相互比較,引用形參是更加安全的寫法哈。
為什么要用引用形參?
1. Using Reference Parameters to Return Additional Information
使用引用形參返回額外的信息
2. Using (const) References to Avoid Copies
利用 const 引用避免復制
compile錯誤信息:
error: invalid initialization of non-const reference of type}
|
所有不可變的引用參數都應該定義成const。非const引用形參缺少靈活性。這樣的形參既不能用 const 對象初始化,也不能用字面值或產生右值的表達式實參初始化。
Reference parameters that are not changed should be references to const. Plain, nonconst reference parameters are less flexible. Such parameters may not be initialized by const objects, or by arguments that are literals or expressions that yield rvalues.
形參是vector
對于vector,傳遞參數應該是C++ 程序員傾向于通過傳遞指向容器中需要處理的元素的迭代器來傳遞容器。(in practice, C++ programmers tend to pass containers by passing iterators to the elements we want to process)
形參是array
首先不能把數組作為形參來傳遞。
數組參數的傳遞方式:
1. 通過指針傳遞數組。
以下形式的定義是等價的,形參都是指向數組的指針
void printValues(int*) { /* ... */ }
void printValues(int[]) { /* ... */ }
void printValues(int[10]) { /* ... */ }
|
2. Passing an Array by Reference。通過引用傳遞數組
容易混淆的關于指針的定義:
int *matrix[10]; // array of 10 pointers
int (*matrix)[10]; // pointer to an array of 10 ints
|
如何在function中確定數組長度?
有三種方法:
1. 第一種方法是在數組本身放置一個標記來檢測數組的結束。C 風格字符串就是采用這種方法的一個例子,它是一種字符數組,并且以空字符 null 作為結束的標記。
2. 使用標準庫規范
傳遞指向數組第一個和最后一個元素的下一個位置的指針。
printValues(const int *beg, const int *end)
{
while (beg != end) {
cout << *beg++ << endl;
}
}
|
3. 明確指出數組的大小。就是定義第二個參數來指明數組的大小。
7.3 返回語句 return statement
函數返回值(return value)
需要定義一個臨時對象-temporary object。這個對象被函數的返回值所初始化。
返回值是引用reference
Example:
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size() ? s1 : s2;
}
string a1 ="123";
string a2 = "12";
//調用
string res = shorterString(a1,a2) ;
|
不能返回對局域對象的引用。
7.4 函數聲明FunctionDeclarations
函數應該在頭文件中聲明,在源文件中實現。
定義函數的源文件中要include函數聲明的頭文件,例如zfmnlcom.h文件中聲明了函數getExpResultFromNarrowCharBuf(),而在zfmnlcom.c文件定義了函數getExpResultFromNarrowCharBuf()
Static Local Objects
特點:
1. 作用范圍是函數內部,in the scope of a function
2. 但是生存期跨越了這個函數的多次調用(whose lifetime persists across calls to the function.)
3. 這類對象并不是在函數被調用時才創建的,而是在調用之前,整個程序初始化時就已經創建成功了。
7.6內聯函數inline Functions
引入內聯函數的目的是為了解決程序中函數調用的效率問題。
一般來說,內聯機制適用于優化小的、只有幾行的而且經常被調用的函數。大多數的編譯器都不支持遞歸函數的內聯。
內聯函數和宏定義的區別?
想到的區別就是如果使用內聯函數在編譯的時候可以進行類型檢查,而宏則不具備這樣的功能。
例如:
#define MAX(a,b) ((a)>(b)?(a):(b))
MAX(a,"Hello"); //錯誤地比較int和字符串,沒有參數類型檢查
|
這在編譯的時候是可以通過的。但是如果使用內聯函數,就會在編譯的時候報錯。
inline int MAX(int a,int b)
{
return a>b?a:b;
}
|
7.7類成員函數
C++中所說的member在Java中是屬性;member function是Operation。member function的聲明必須在類定
關于this
每個成員函數都有一個參數this。this是和成員函數相關聯的,而不是和Object相關聯。this實際上是一個指針,指向的是調用此函數的對象。和java不同,java則是Object。
const成員函數
const 改變了隱含的 this 形參的類型。因此調用這個函數的對象也必須是const的。
Example:
bool same_isbn(const Sales_item &rhs) const
{ return isbn == rhs.isbn; }
|
構造函數
構造函數列表
Example:
Sales_item(): units_sold(0), revenue(0.0) { }
|
類代碼文件的組織構成
l 類聲明文件放在頭文件header中,type.h。
l 成員函數的定義放在源文件里source file。并且這個文件一定要include上面的頭文件。
7.8. 重載函數Overloaded Functions
overload means: same function name but different parameter list.
重載與作用域Overloading and Scope
重載必須是在相同的作用域里。
Example:
void print(const string &);
void print(double); // overloads the print function
void fooBar(int ival)
{
//注意:這不是重載,它會隱藏前面所有的print()函數定義。作用域不同
void print(int); // new scope: hides previous instances of print
print("Value: "); // error: print(const string &) is hidden
print(ival); // ok: print(int) is visible
print(3.14); // ok: calls print(int); print(double) is hidden
}
|
函數匹配和實參轉換
這部分給出的例子真是引人入勝,讀起來一氣呵成。初看上去,我想答案該是調用f(int);
void f();
void f(int);
void f(int, int);
void f(double, double = 3.14);
f(5.6); // calls void f(double, double)
|
錯了!
函數匹配是分成3步完成的:
1. 確定候選函數Candidate Functions,篩選條件:函數名稱相同的函數。
2. 選擇可行函數Determining the Viable Functions,篩選條件:參數的數量和參數的類型(或者是可轉換的類型)匹配的函數。
尤其要特別注意函數具有默認實參default arguments的情況。
3. 找到最匹配的 ,篩選條件:實參和形參之間互相類型最接近。
什么是“最匹配”的?
l 其每個實參的匹配都不劣于其他可行函數需要的匹配。
l 至少有一個實參的匹配優于其他可行函數提供的匹配。
其實這個問題有些擰巴了,在實際開發中,我們應該避免出現這樣的情況。
轉換等級以降序排列
1. An exact match. The argument and parameter types are the same.
精確匹配。實參與形參類型相同。
2. Match through a promotion.
通過類型提升實現的匹配。
3. Match through a standard conversion.
通過標準轉換實現的匹配。
4. Match through a class-type conversion.
通過類類型轉換實現的匹配。
形參匹配和枚舉Enumerations
規則:An integral object that happens to have the same value as an enumerator cannot be used to call a function expecting an enum argument. 整數對象即使具有與枚舉元素相同的值也不能用于調用期望獲得枚舉類型實參的函數。
Overloading and const Parameters重載和 const 形參
const形參只在形參類型是指針或者引用的時候才有影響。
可基于函數的形參是指向 const 對象還是指向非 const 對象,實現函數重載。
Record lookup(Account&);
Record lookup(const Account&); // new function
const Account a(0);
Account b;
lookup(a); // calls lookup(const Account&)
lookup(b); // calls lookup(Account&)當傳遞的實參是non const,兩個函數都是有效的,但是要根據最佳匹配原則來匹配,把const引用指向non const對象要進行類型轉換
|
引用和指針的道理是一樣的。指針是指向const object還是nonconst object。注意這里并不是說指針是不是const的。
f(int *);
f(const int *);
|
7.9 指向函數的指針
函數類型取決于以下的2個條件
1. 返回值類型。return type
2. 參數列表。its parameter list
函數指針定義:
//pf是指向函數的指針,它所指向的函數帶有兩個 const string& 類型的形參和 bool 類型的返回值。
bool (*pf)(const string &, const string &);
|
括號是必須的。
簡化函數指針定義:
// cmpFcn is the name of a type that is a pointer to function. cmpFcn 是一種指向函數的指針類型的名字。
typedef bool (*cmpFcn)(const string &, const string &);
…
// 使用,定義和初始化
cmpFcn pf1 = 0; // ok: unbound pointer to function
cmpFcn pf2 = lengthCompare //直接使用函數的名稱而不是調用它,這個名稱則是指向函數的指針
|
使用指針調用函數:
pf2("hi", "bye"); // equivalent call: pf1 implicitly dereferenced
(*pf2)("hi", "bye"); // equivalent call: pf1 explicitly dereferenced
|
函數指針也可以作為函數的形參
void useBigger(const string &, const string &,
bool(const string &, const string &));
void useBigger(const string &, const string &,
bool (*)(const string &, const string &));
|
返回值也可以是函數指針:
int (*ff(int))(int*, int);
|
不得不說,抓狂啊。這種恐怖的寫法能夠正確寫出來就已經是強人了J
理解:
ff(int)
函數ff,有1個int形參。
int (*)(int*, int);
返回值是1個函數指針,這個函數指針指向的函數的有2個參數int*, int并且返回值是int。
不過如果使用typedef,就好理解一些哈
typedef int (*PF)(int*, int);
PF ff(int); // ff returns a pointer to function
|
函數類型可以作為形參,但是只有函數指針能夠作為返回值。