1. 程式人生 > >C++ Primer筆記 六 函式

C++ Primer筆記 六 函式

一個典型的函式包含三個基礎部分:返回型別函式名字形參

而這個三個部分可以說構成了函式的一個介面,當我們使用函式的時候,三個基礎部分的不同就代表了代表函式的能夠做的實現的不同。

為了清除函式的具體作用,首先應引入宣告週期和區域性物件的兩個概念,每個物件在程式執行中都有自己的宣告週期。

而名字的作用域也就是這個名字作為某一個物件的生存空間是程式文字的一部分,名字在這個部分,是可見的。

而物件的生存週期是程式執行的過程中,該物件存在的一段時間。

形參和函式體內定義的變數統稱為區域性變數,他們僅在函式的作用域內可見。但當他們可見的時候,就會隱藏外層作用域中的同名物件,知道推出區域性變數的作用域時,外層作用的同名物件才得以顯示和恢復。

區域性物件中,如果一個物件在函式執行路徑經過時建立,在該定義到達所在的程式碼塊的末尾時銷燬,那麼我們稱這個物件是自動物件。

形參時一種自動物件,如果我們對於自動變數的定義的本身含有初始值,那麼就用這個初始值進行初始化;如果我們對於變數本身的定義不含初始值,那麼將執行預設的初始化,而因為自動變數本身是區域性變數,如果是內建型別的自動變數,其初始化的值本身是沒有意義的。

有時,我們需要在內部定義的變數在整個外部作用域存在,那麼就需要使用區域性靜態變數,區域性靜態變數的設定是在變數宣告的前面加上關鍵字static,此時該物件將在函式退出時繼續存在,並且只在第一次函式執行路徑這個宣告的時候進行初始化。

有時,我們需要額外返回除了返回物件的另一個引數,一個方法是使用隱式返回形參變數,方法是在進行函式宣告的時候,多加一個引用形參。

注意:當用實參初始化形參的時候,將自動忽略頂層const,這在使用函式過載的時候要格外小心。

指標或引用形參和const☆

形參的初始化和變數的初始化其實是一樣的方式,我們可以通過一個非常量來初始化一個底層const的物件,但是反過來說是不行的,即我們不能用一個有底層const的物件來初始化一個非常量。

同時,一個普通的引用必須用同類型的物件進行初始化。

1.不能用const引用來初始化一個普通的引用。

2.不能用字面值來初始化一個普通的引用。

在過載中,有時我們可能需要用一個const引用來初始化一個普通引用,或者用一個具有底層const的物件來初始化一個非常量(指標或引用),那麼此時我們可以選擇使用const_cast

函式來進行(去const)轉化。

而一個成熟的程式中,應當儘可能少使用型別轉換,我們要確定的規則是,儘可能的多使用const reference即長量引用。

陣列

對於函式的形參,因為陣列本身的特性,會使我們在使用和定義的時候遇到麻煩;

因為:

1.不允許拷貝陣列

2.在使用陣列的時候,通常會將其轉化為指標。

儘管,陣列在作為形參的時候會被轉化為指標,我們依然可以用陣列的形式進行函式的宣告。

如:1. void print(const int*);

2. void print(const int[]);

3. void print(const int[10]);

這三者的宣告是完全等價的,其實都等同於具有唯一的形參const int*。因此,我們可以看出 3 中的陣列的大小並沒有實際的作用。

正因為陣列的大小在作為形參的使用沒有意義,我們在處理陣列的時候又需要知道準確的陣列大小的資訊。就需要我們額外新增關於陣列大小的資訊。

一般來講,管理指標形參有三種方式:

一、使用標記指定陣列的長度

這個方法需要陣列本身含有一個結束標記,如C風格的字串,其最後一個元素為\0,那麼我們在處理資訊的時候,就可以使用while(*cp)判斷是否達到末尾。

二、使用標準庫的規範

使用傳遞指向陣列首元素和尾後元素指標的方法,如使用begin()和end()函式。

三、顯式地傳遞一個表示陣列大小的形參

關於陣列,因為C++允許將變數定義為陣列的一個引用,所以,形參也可以是陣列的引用。

如:void print(int (&arr1) [10]){

for (auto elem:arr1)

cout<<elem<<endl;

}

而此時的陣列的大小在形參中確實具有意義,這也表明我們使用這種形式的時候,必須注意對陣列的操作不能越界。

可變形參的函式

有時候,我們需要對一個函式指定不定個數的形參,一般來講,這些形參很可能具有相同的型別,此時,我們就需要用到C++11提供的標準庫型別:initializer_list型別。

initializer_list<T> 1st;   預設初始化,列表為空。

函式宣告: void error_msg(initializer_list<string> 1st)

函式呼叫:error_msg({"function x",expected,actual});  (務必記得大括號的列表化初始方法

返回值型別

仍然需要我們再提一遍,函式返回一個區域性變數或者區域性變數的引用是錯誤的,因為在函式執行結束的時候,區域性變數已經被銷燬,我們需要知道的是在我們返回時,所用或者所引的物件是在函式之前已經存在還是在函式中建立的?

如果函式返回了一個指標、引用或者類型別,我們就可以通過這些型別的相關操作,來呼叫函式的返回結果。

如:auto sz=short(s1,s2).size();

上面的運算是呼叫了short返回的類型別的成員運算子,獲得返回值的長度大小資訊。

注意:我們有時候要知道一個函式返回的是左值還是右值?那麼怎麼判斷呢?其實很簡單,當一個函式返回一個物件的引用的時候,這個返回的值就是左值,其餘的是右值。

當返回左值的時候,我們可以像使用左值一樣來使用函式的返回結果,特別的是,當返回一個非常量的左值的時候,我們甚至可以為其賦值。

如: get_val(s , 0) ='A';            get_val(s , 0)返回 s[0]的引用。