1. 函數(shù)基礎(chǔ)
形參和實(shí)參
實(shí)參是形參的初始值. 形參和實(shí)參類型要一致, 順序要對(duì)應(yīng). (但編譯器具體按什么順序求值則不確定)
局部對(duì)象
形參和函數(shù)內(nèi)部的變量是局部變量, 僅在函數(shù)作用域可見(jiàn).
所有函數(shù)體之外的對(duì)象存在于整個(gè)程序的生命周期, 直到程序退出.
自動(dòng)對(duì)象
對(duì)于在函數(shù)執(zhí)行時(shí)創(chuàng)建對(duì)象, 函數(shù)退出時(shí)銷毀的對(duì)象稱為 自動(dòng)對(duì)象.
形參也是自動(dòng)對(duì)象, 函數(shù)開(kāi)始時(shí)為其申請(qǐng)空間, 函數(shù)退出時(shí)形參也被銷毀.
局部靜態(tài)對(duì)象
使用static
聲明的局部對(duì)象稱為 局部靜態(tài)對(duì)象. 這種對(duì)象在函數(shù)執(zhí)行時(shí)創(chuàng)建, 但函數(shù)退出時(shí)卻不銷毀, 直到程序退出時(shí)才銷毀**, 生命周期貫穿整個(gè)程序.
函數(shù)聲明
函數(shù)必須在使用前聲明
函數(shù)可以有多次聲明, 但只能有一次定義. 函數(shù)聲明也稱為 函數(shù)原型.
2. 函數(shù)參數(shù)
參數(shù)傳遞
- 引用傳遞, 形參相當(dāng)于實(shí)參的別名
- 值傳遞, 形參和實(shí)參是獨(dú)立的, 實(shí)參拷貝給形參
- 指針傳遞, 拷貝的指針的地址, 可以通過(guò)指針修改對(duì)象, 但形參和實(shí)參的指針地址是不同的
const形參和實(shí)參
形參有頂層const時(shí), 實(shí)參傳遞常量對(duì)象或非常量對(duì)象都可以
// 變量初始化和形參初始化對(duì)比
int i = 0;
const int *cp = &i; // 正確, 但cp不能改變i
const int &r = i; // 正確, 但r不能改變i
const int &r2 = 42; // 正確
int *p = cp; // 錯(cuò)誤, p的類型和cp類型不匹配
int &r3 = r; // 錯(cuò)誤, r的類型和r3類型不匹配
int &r4 = 42; // 錯(cuò)誤, 不能用字面值初始化一個(gè)引用
int i = 0;
const int ci = i;
string::size_type ctr = 0;
reset(&i); // 調(diào)用參數(shù)是int*的reset
reset(&ci); // 錯(cuò)誤, 不能用指向const int的對(duì)象初始化int *
reset(i); // 調(diào)用參數(shù)時(shí)int&的reset
reset(ci); // 錯(cuò)誤, 不能把普通引用綁定到const上
reset(42); // 錯(cuò)誤, 不能把普通引用綁定到字面值上
reset(ctr); // 錯(cuò)誤, 類型不匹配, size_t是無(wú)符號(hào)類型
盡量使用常量引用
數(shù)組形參
數(shù)組兩個(gè)特殊的性質(zhì)
- 數(shù)組不允許拷貝, 意味著不能通過(guò)值傳遞一個(gè)數(shù)組實(shí)參
- 數(shù)組會(huì)被轉(zhuǎn)化為指針, 意味著傳遞一個(gè)數(shù)組實(shí)參時(shí), 實(shí)際上傳遞的首元素指針
傳遞方式
- 使用標(biāo)準(zhǔn)庫(kù)規(guī)范
void print(const int *beg, const int *end)
{
while(beg != end)
cout << *beg++ << endl;
}
int j[2] = {0, 1};
print(begin(j), end(j));
- 顯式傳遞一個(gè)數(shù)組大小的實(shí)參數(shù)
void print(const int ia[], size_t size)
{
for (size_t i = 0; i < size; i++)
cout << ia[i] << endl;
}
int j[2] = {0, 1};
print(j, end(j) - begin(j));
當(dāng)函數(shù)不需要寫操作, 數(shù)組的形參應(yīng)該是const類型的指針.
- 數(shù)組引用形參
void print(int (&arr)[10])
{
for (auto elem: arr)
cout << elem << endl;
}
&arr 括號(hào)不能少, 因?yàn)?f(int &arr[10]) 表示arr是引用的數(shù)組, f(int (&arr)[10]) 是具有10個(gè)整數(shù)的數(shù)組引用.
傳遞多維數(shù)組
c++沒(méi)有真正的多維數(shù)組, 多維數(shù)組在c++中是數(shù)組的數(shù)組, 因此和數(shù)組一樣, 多維數(shù)組指向數(shù)組的首元素
void print(int (*metrix)[10), int rowSize) { /**...**/};
main函數(shù)參數(shù)
- argc 表示元素?cái)?shù)量
- **argv[] 表示多個(gè)c風(fēng)格字符串的數(shù)組, 具體有多少個(gè)由argc決定
可變形參函數(shù)
initializer_list
是標(biāo)準(zhǔn)庫(kù)類型, 表示特定類型值的數(shù)組
void err_msg(int err_code, initializer_list<string> errors) {
cout << "code " << err_code;
for (auto beg = erros.begin(); beg != errors.end(); beg++)
cout << *beg << " ";
cout << endl;
}
// initializer_list形參在傳遞實(shí)參時(shí), 需要傳遞一個(gè)列表值
err_msg(500, {"e1", "e2", "e3"})
c++兼容了c的省略符號(hào)形參和varargs void f(param_list, ...);
3. 函數(shù)返回值
引用返回左值
char &get_val(string &str, string::size_type ix)
{
return str[ix];
}
int main()
{
string a("value");
cout << a << endl;
// get_val返回左值引用
get_val(a, 0) = 'V';
cout << a << endl;
}
列表初始化返回值
vector<string> process()
{
//...
return {"a", "b"};
}
返回?cái)?shù)組指針
由于數(shù)組不能拷貝, 因此返回?cái)?shù)組返回的是數(shù)組指針, 這種返回類型函數(shù)的聲明方式可以有以下幾種
- 使用typedef或using
typedef int arrT[10];
using arrT = int[10]; // 和typedef等價(jià)
arrT *func(int i); // 返回一個(gè)指向10個(gè)整數(shù)的數(shù)組的指針
- 不使用類型別名的聲明
語(yǔ)法Type (*function (parameter_list))[dimension]
上面的可以聲明為int (*func (int i))[10]
分解聲明的每個(gè)部分的含義:
- func (int i), 表示函數(shù)調(diào)用需要一個(gè)int類型的實(shí)參
- (func(int i)), 表示對(duì)調(diào)用函數(shù)可以執(zhí)行解引用*操作
- (*func (int i))[10], 表示調(diào)用func 解引用 得到一個(gè)大小是10的數(shù)組
- int (*func (int i))[10], 表示數(shù)組的類型是int
- 使用尾置返回類型(c++11)
auto func(int i);
- 使用decltype (c++11)
** 前提是知道了返回的數(shù)組指針指向哪個(gè)數(shù)組**
int odd[] = {1, 3, 5};
int even[] = {2, 4, 6};
decltype(odd) *arrPtr(int i)
{
return (i % 2) ? odd : event;
}
4. 函數(shù)重載
main函數(shù)不能重載
重載函數(shù)是名字相同, 但形參類型不同的一組函數(shù)集合
Record lookup(const Account&);
Record lookup(const Phone&);
Record lookup(const Name&);
const影響重載的識(shí)別嗎
頂層const形參不會(huì)認(rèn)為是不同的參數(shù)
Record lookup(Phone);
Record lookup(const Phone); // 重復(fù)聲明
Record lookup(Phone *);
Record lookup(Phone * const); // 重復(fù)聲明
底層const實(shí)現(xiàn)函數(shù)重載
Record lookup(Account &);
Record lookup(const Phone&); // 新函數(shù), 常量引用
Record lookup(Account *);
Record lookup(const Account *); // 新函數(shù), 常量指針
const_cast和重載
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size()? s1 : s2;
}
// 重載函數(shù), 非常量引用
string &shorterString(string &s1, string &s2)
{
// s1和s2都是非常量引用, 通過(guò)const_cast轉(zhuǎn)為常量引用, 調(diào)用了shorterString重載函數(shù)
auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2));
// 重載函數(shù)返回的類型是常量引用, 通過(guò)const_cast轉(zhuǎn)為非常量引用返回
return const_cast<string&>(r);
}
作用域和重載
- 作用域內(nèi)部不能重載函數(shù)名, 否則會(huì)造成未定義的行為
- 作用域外重載函數(shù), 作用域內(nèi)不受影響, 正常使用
5. 函數(shù)特殊特性
默認(rèn)實(shí)參
應(yīng)該在函數(shù)聲明中就聲明默認(rèn)實(shí)參, 并放在合適的頭文件中
默認(rèn)實(shí)參行為: 調(diào)用函數(shù)沒(méi)有傳遞參數(shù)則使用默認(rèn)實(shí)參
默認(rèn)實(shí)參聲明
string screen(sz = 24, sz = 80, char);
默認(rèn)實(shí)參聲明一個(gè)參數(shù)只能聲明一次
string screen(sz, sz, char = '');
string screen(sz, sz, char = '*'); // 錯(cuò)誤, 重復(fù)聲明
constexpr函數(shù)
constexpr變量定義時(shí)如果用到了函數(shù), 則函數(shù)必須也是constexpr函數(shù)
char *s = "hello";
constexpr size_t size = strlen(s) + 1; // 錯(cuò)誤, strlen不是constexpr函數(shù)
constexpr int new_size() { return 42; }
constexpr int foo = new_size() + 1; // 正確
constexpr size_t scale(size_t cnt) { return new_sz() * cnt };
int arr[scale(2)]; // 正確scale是常量表達(dá)式, 傳遞的cnt實(shí)參也屬于常量
int i = 2;
int a2[scale(i)]; // 錯(cuò)誤, i不是constexpr
內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)聲明
inline const string &shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size()? s1 : s2;
}
聲明內(nèi)聯(lián)函數(shù), 編譯器會(huì)嘗試展開(kāi)函數(shù), 但不包裝聲明inline
的函數(shù)100%做成內(nèi)聯(lián)的.
內(nèi)聯(lián)函數(shù)適合優(yōu)化規(guī)模較小、流程直接、頻繁調(diào)用的函數(shù)
調(diào)試幫助
assert預(yù)處理宏
assert(expr)
如果expr為真, 輸出信息并終止程序執(zhí)行, 如果為假什么也不做
assert宏常用于檢測(cè)不能發(fā)生的條件預(yù)處理宏
-
__func__
, 函數(shù)名 -
__FILE__
, 文件名 -
__LINE__
, 行號(hào) -
__TIME__
, 文件編譯時(shí)間 -
__DATE__
, 文件編譯日期
6. 函數(shù)指針
函數(shù)指針聲明
// 原型函數(shù)
bool lengthCompare(const string &, const string &);
bool (*pf)(const string &, const string &);
使用函數(shù)指針
// 原型函數(shù)
pf = lengthCompare;
pf = &lengthCompare; // 取地址符是可選的
重載函數(shù)指針
重載函數(shù)可以有對(duì)應(yīng)的函數(shù)指針, 編譯器會(huì)決定選擇哪個(gè)函數(shù)
指針作為函數(shù)形參
void userBigger(const string &s1, const string &s2,
bool pf(const string&, const string &);
void userBigger(const string &s1, const string &s2,
bool (*pf)(const string&, const string &); // 等價(jià)聲明, 函數(shù)名會(huì)自動(dòng)轉(zhuǎn)換
返回函數(shù)指針
- 使用typedef或using
using F = (int *, int); // F是函數(shù)類型
using PF = int(*)(int *, int); // PF是函數(shù)指針類型
直接聲明
int(*f1(int))(int *, int);
使用auto
auto f1(int);
使用decltype
string::size_type sumLength(const string&, const string &);
decltype(sumLength) *getFn(const string &); // 返回類型是一個(gè)函數(shù)指針