C++ Primer: Functions

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ì)

  1. 數(shù)組不允許拷貝, 意味著不能通過(guò)值傳遞一個(gè)數(shù)組實(shí)參
  2. 數(shù)組會(huì)被轉(zhuǎn)化為指針, 意味著傳遞一個(gè)數(shù)組實(shí)參時(shí), 實(shí)際上傳遞的首元素指針

傳遞方式

  1. 使用標(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));
  1. 顯式傳遞一個(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類型的指針.

  1. 數(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ù)的聲明方式可以有以下幾種

  1. 使用typedef或using
typedef int arrT[10];

using arrT = int[10]; // 和typedef等價(jià)
arrT *func(int i); // 返回一個(gè)指向10個(gè)整數(shù)的數(shù)組的指針
  1. 不使用類型別名的聲明
    語(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
  1. 使用尾置返回類型(c++11)
auto func(int i);
  1. 使用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)試幫助

  1. assert預(yù)處理宏
    assert(expr) 如果expr為真, 輸出信息并終止程序執(zhí)行, 如果為假什么也不做
    assert宏常用于檢測(cè)不能發(fā)生的條件

  2. 預(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ù)指針

  1. 使用typedef或using
using F = (int *, int); // F是函數(shù)類型
using PF = int(*)(int *, int); // PF是函數(shù)指針類型
  1. 直接聲明
    int(*f1(int))(int *, int);

  2. 使用auto
    auto f1(int);

  3. 使用decltype

string::size_type sumLength(const string&, const string &);
decltype(sumLength) *getFn(const string &); // 返回類型是一個(gè)函數(shù)指針
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市囊榜,隨后出現(xiàn)的幾起案子违崇,更是在濱河造成了極大的恐慌雨膨,老刑警劉巖留量,帶你破解...
    沈念sama閱讀 221,406評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吼具,死亡現(xiàn)場(chǎng)離奇詭異雄人,居然都是意外死亡从橘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門础钠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)恰力,“玉大人,你說(shuō)我怎么就攤上這事旗吁∥矗” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,815評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵阵漏,是天一觀的道長(zhǎng)驻民。 經(jīng)常有香客問(wèn)我,道長(zhǎng)履怯,這世上最難降的妖魔是什么回还? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,537評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮叹洲,結(jié)果婚禮上柠硕,老公的妹妹穿的比我還像新娘。我一直安慰自己运提,他們只是感情好蝗柔,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,536評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著民泵,像睡著了一般癣丧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上栈妆,一...
    開(kāi)封第一講書(shū)人閱讀 52,184評(píng)論 1 308
  • 那天胁编,我揣著相機(jī)與錄音,去河邊找鬼鳞尔。 笑死嬉橙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的寥假。 我是一名探鬼主播市框,決...
    沈念sama閱讀 40,776評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼糕韧!你這毒婦竟也來(lái)了枫振?” 一聲冷哼從身側(cè)響起喻圃,我...
    開(kāi)封第一講書(shū)人閱讀 39,668評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒋得,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體乒疏,經(jīng)...
    沈念sama閱讀 46,212評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡额衙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,299評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怕吴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窍侧。...
    茶點(diǎn)故事閱讀 40,438評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖转绷,靈堂內(nèi)的尸體忽然破棺而出伟件,到底是詐尸還是另有隱情,我是刑警寧澤议经,帶...
    沈念sama閱讀 36,128評(píng)論 5 349
  • 正文 年R本政府宣布斧账,位于F島的核電站,受9級(jí)特大地震影響煞肾,放射性物質(zhì)發(fā)生泄漏咧织。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,807評(píng)論 3 333
  • 文/蒙蒙 一籍救、第九天 我趴在偏房一處隱蔽的房頂上張望习绢。 院中可真熱鬧,春花似錦蝙昙、人聲如沸闪萄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,279評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)败去。三九已至,卻和暖如春烈拒,著一層夾襖步出監(jiān)牢的瞬間为迈,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,395評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工缺菌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留葫辐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,827評(píng)論 3 376
  • 正文 我出身青樓伴郁,卻偏偏與公主長(zhǎng)得像耿战,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子焊傅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,446評(píng)論 2 359