1. partone---第六章 函數

fig1.jpg

函數是一個命名了的代碼塊,可有0個或多個參數。


6.1 函數基礎

局部對象

  • 自動對象: 存在于塊執行期間,當塊執行完畢時,則內存消失。例如函數的形參。
  • 局部靜態對象: 生命周期為第一次定義到程序結束才終止。

函數聲明

函數聲明應該放到頭文件中。 這樣做的好處在于:同一函數的所有聲明一致;想改變函數的接口,只需要改變函數的聲明的即可。


6.2 參數傳遞

引用傳遞: 形參的類型決定了形參和實參的交互方式。 當形參是引用類型時,如其他引用一樣,引用形參是對應實參的別名。
值傳遞: 將實參的值拷貝給形參,此時形參和實參是兩個相互獨立的對象。對形參的改變,不會影響到實參的值。

6.2.1 值傳遞

*指針形參

void reset(int *ip)
{
  *ip=0;
  ip=0;
}
int i=42;
reset(&i)

此時形參和實參是兩個相互獨立的對象。
實現兩個數交換的兩種方法(C++中,建議使用引用類型代替指針形參)

 void swap1(int *a,int * b)//指針 參數是兩個指針變量
{
   int tmp;
   tmp=*a; //把a指向的值賦給tmp
   *a=*b;   //把b指向的值賦給a指向的值
   *b=tmp;   //把tmp的值賦給b指向的值
   //這樣就達到了變換a,b指向的值的目的
 }
void swap2(int &a,int &b)//引用 參數是兩個整型變量的引用
{
//引用就是他本身的值,所以直接交換兩個的值就行了。
       int tmp;
       tmp=a;
       a=b;
       b=tmp;
}

6.2.2 引用傳遞

使用引用避免拷貝

當拷貝大的類類型對象或容器對象時,比較低效。

使引用可返回額外信息

一個return只能返回一個參數,通過引用帶出多個參數。

6.2.3 const形參和實參

void fcn(const int i){};
void fcn(int i){};

上面這兩個函數是一樣的,不能重載。因為當形參有頂層const時,傳給它的是const或非const均可以
頂層const:指針本身是一個常亮;
底層const:指針所指的對象是一個常量。

const  int ci=42;   //頂層const

指針或引用形參與const

非常量可以初始化一個底層const對象,反之則不行
同時,一個普通類型的引用,只能用同類型的對象初始化。

盡量使用常量引用

頂層const作為形參時,實參傳遞是const或非const均可;

例如:
bool  is_empty(string &s)
{
  return s.empty();
}

這種情況下,限制了該函數所能接受的實參類型,無法把const、對
象、字面值常量或者需要類型轉換的對象傳遞給普通的引用形參。
另一方面,也可能帶來誤導,可修改字符串s。
應該把形參改為 const string &s

6.2.4 數組形參

數組的兩個特性:不允許拷貝數組、數組名轉換為指針

6.2.5 main:處理命令行選項

int main(int argc, char *argv[])
{
    //其中argc是argv[][]的行數
}

6.2.6 含有可變形參的函數---initializer_lsit

initializer_lsit是一種標準庫類型。適用于函數的實參未知,但類型都相同的情況下。

int icount(initializer_lsit<int> il)
{
  int count =0;
  for(auto val :il)
  {
      count += val;
  }
  return count;
}
icount({1,2,3,4,5});

6.3 返回類型和return語句

6.3.1 無返回值函數

函數的返回類型是void時,如果函數末尾沒有return時,也可以。因為在函數的最后一句會隱式的調用return

6.3.2 有返回值函數

return exp;

不要返回局部對象的引用或指針

函數終止后,局部變量的引用將不再指向有效的內存區域。

引用返回左值
char &get_val(string &str, int index)
{
    return str[index];
}

void main
{
  string s("a value");
  get_val(s,0) = 'A';  //改為 A value
}

6.3.3 返回數組的指針

int (*func(int i))[10];


6.4 函數重載

重載函數:名字相同,但是函數參數列表不同。
注:返回類型不算。

重載和const形參

頂層const
(因為當形參有頂層const時,傳給它的是const或非const均可以)。

(因為當形參有頂層const時,傳給它的是const或非const均可以),所以下面兩組函數是重復聲明。
Record lookup(phone);
Record lookup(const phone);

Record lookup(phone*);
Record lookup(phone* const );

形參是指針或者引用。將是底層const,可通過指向的對象時常量對象還是非常量對象實現重載。
一方面,const不能轉換為其他類型,只能將const轉換為const;另一方面,相反的,非const可以轉換為const,但是當同時存在非常量和const兩個版本的形參時,編譯器會優先選擇非常量版本的函數,

Record lookup(Account &);
Record lookup(const Account &);

Record lookup(phone*);
Record lookup(phone* const );

6.5 特殊用途語言特性

6.5.1 默認實參

規定:一旦某個形參被賦予了默認值,那么它后面的所有形參都必須被賦予默認值。

6.5.2 內聯函數和constexpr函數

將函數指定為內聯函數,通常就是在每個調用點進行展開。
constexpr函數---暫時用不到。

6.5.3 調試幫助

assert預處理

如:assert(expr),如果表達式為真,什么都不做;如果表達式為假,則輸出信息并終止程序。

DEBUG 預處理命令

如果你把代碼夾在#ifdef DEBUG 和對應的 #endif 中間,那么這段代碼只有在調試(DEBUG)下才會被編譯。也就是說,如果你在RELEASE模式下,這些代碼根本就不會存在于你的最終代碼里頭。

 #include <iostream>
 using namespace std; 
 #ifdef DEBUG
 inline void msg()
{ 
 cout<<"I'm testing"; 
} 
 #else 
inline void msg() {} 
 #endif 
int main()
 {
   msg();
   return 0;
 }

6.6 函數匹配

背景:有幾個重載函數,當一個調用函數調用重載函數時,可能會發生二義性。
解決辦法:尋求最佳匹配。


6.7 函數指針

bool lengthCompare(const string &,const string &);

  1. 該函數的函數類型為bool lengthCompare(const string &,const string &)。 函數類型,是由形參列表和返回值決定,函數名稱只是函數的別名而已。
  2. 聲明一個函數指針,只需要使用指針代替函數即可(因為函數名稱僅僅是一個別名)
    bool (*pf) (const string &,const string &)
  3. 如何使用函數指針
    當把函數名作為一個值使用時,該函數會自動的轉換為指針。
    指向不同的函數指針之間,是不能轉換的,因為代表的函數返回值。
pf = lengthCompare;   // 指向名為lengthCompare的函數
pf = &lengthCompare; //等價,取址操作符是可選的。

//直接使用
bool b1 = pf("hello", "world");
bool b2 = (*pf) ("hello", "world"); //等價調用
  1. 函數指針形參
//第三個形參是函數類型,自動轉換為指向函數的指針
void useBigger(const string &, const string &, bool  pf (const string &,const string &));
|
void useBigger(const string &, const string &, bool  (*pf) (const string &,const string &)); //自動轉換為函數指針

如上面,這樣顯得函數冗長。
通常:
typedef bool Func(const string &, const string &) ;
void useBigger(const string &, const string &, Func));

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容