【C++學習筆記】函數返回和函數重載

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/LAW_130625/article/details/79003165

一、返回類型和return語句
  return語句終止當前執行的函數並將控制權返回到調用該函數的地方,return語句有兩種形式:

returnreturn expression;

1.1 無返回值函數
  沒有返回值的return語句只能用在返回類型是void的函數中,按時返回void的函數不要求非得有return語句,因爲這種函數最後一句會隱式地執行return。void函數如果想在它中間位置提前退出,可以使用return語句,這種做法類似於break語句退出循環。

void swap(int &v1, int &v2)
{
   //如果兩個值相等,則不需要交換,直接退出
   if (v1 == v2)
      return;
   int tmp = v2;
   v2 = v1;
   v1 = tmp;
   //此處無需顯示的return語句
}

  一個返回類型是void的函數也能使用return語句的第二種形式,不過此時return語句的expression必須是另一個返回void的函數,強令void函數返回其他類型的表達式將產生編譯錯誤。

1.2 有返回值函數
  只要函數的返回類型不是void,那麼該函數的每條return語句必須返回一個值,並且返回值的類型必須與函數返回類型相同,或者能隱式地轉換成函數的返回類型。
  ①值是如何被返回的
  返回一個值的方式和初始化一個變量或者形參的方式完全一樣,返回的值用於初始化調用點的一個臨時量,即函數調用的結果。

//如果ctr的值大於1,返回word的複數形式
string make_plu(size_t ctr,const string &word,const string &ending)
{
   return (ctr>1) ? word + ending : word
}

  由於該函數的返回類型是string,意味着返回值被拷貝到調用點,即返回word的副本或者一個未命名的臨時string對象,其內容爲word和ending的和。
  同其他引用類型一樣,如果函數返回引用,則該引用僅是它所引對象的一個別名,如下,函數的形參和返回類型都是const string的引用,所以不管調用函數還是返回結果都不會真正拷貝string對象。

const string &shortStr(const string &s1,const string &s2)
{
   return s1.size() <= s2.size() ? s1:s2;
}

  ②不要返回局部對象的引用或指針
  因爲函數完成後,它所佔用的存儲空間也隨之被釋放掉,所以函數終止意味着局部變量的引用將指向不再有效的內存區域:

const string &mainp()
{
   string ret;
   if (!ret.empty())
      return ret;           //錯誤,返回局部對愛那個的引用
   else
      returnEmpty";       //錯誤,"Empty"是一個局部臨時量

  上面的return語句都返回未定義的值,即試圖適應mainp函數的返回值引發未定義的行爲。第一條return返回的是局部對象的引用;第二條return語句返回的是字符串字面值轉換成一個局部臨時string對象,也是局部的,當函數結束時臨時對象佔用的空間也隨之釋放,那麼這兩條return語句都指向了不再可用的內存空間。

  ③引用返回左值
  函數的返回類型決定函數調用是否是左值,調用一個返回引用的函數得到左值,其他返回類型得到右值,可以像使用其他左值那樣來使用返回引用的函數的調用,特別是我們能爲返回類型是非常量引用的函數的結果賦值:

char &get_val(string &str,string::size_type ix)
{
   return str[ix];
}
int main()
{
   string s("a value");
   cout << s << endl;
   get_val(s,0) = "A";          //將s[0]的值改爲A
   cout << s << endl;
   return 0;
}

  上面把函數調用放在賦值語句的左側,因爲返回值是引用,因此調用是個左值,與其他左值一樣也能出現在賦值預算符的左側,但是如果返回類型是常量引用,那麼就不能給調用的結果賦值。

  ④列表初始化返回值
  函數可以返回花括號包圍的值的列表,與其他返回結果類似,列表也用來對錶示函數返回的臨時量進行初始化,如果列表爲空,臨時量執行值初始化,否則返回的值由函數的返回類型決定。

vector<string> procss()
{
   //...
   //expected和actual是string對象
   if (expected.empty())
      return {};                   //返回一個空vector對象
   else if (expected == actual)
      return {"functionX","okay"); //返回列表初始化的vector對象
   else
      return {"functionX",expected,actual};

  如果函數返回的是內置類型,則花括號包圍的列表最多包含一個值,且該值所佔空間不大於目標類型的空間。如果返回的是類類型,由類本身定義初始值如何使用。

  ⑤主函數main函數的返回值
  如果函數的返回類型不是void,那麼它必須返回一個值,但是可以允許main函數沒有人突然語句直接結束,因爲編譯器會隱式地插入一條返回0的return語句。main函數返回0表示執行成功,返回其他值表示執行失敗。爲了使返回值與機器無關,cstdlib頭文件定義了連個預處理變量來分別表示成功與失敗,因爲它們是預處理變量,所以既不能在前面加上std::,也不能在using聲明中出現。

int main()
{
   if (some_failure)
      return EXIT_FAILURE;    //定義在cstdlib頭文件中
   else
      return EXIT_SUCCESS;    //定義在cstdlib頭文件中

1.3 返回數組指針
  因爲數組不能被拷貝,所以函數不能返回數組,但是函數可以返回數組的指針或引用。

typedef int arrT[10];         //arrT是一個類型別名,表示的類型是含有10個整數的數組
using arrT = int[10];         //arrT的等價聲明
arrT* func(int i);            //func返回一個指向含有10個整數的數組的指針

  其中arrT是含有10個整數的數組的別名,因此我們無法返回數組,所以返回類型定義成數組的指針,所以func函數接收一個int實參,返回一個指向包含10個整數的數組的指針。
  ①聲明一個返回數組指針的函數
  如果在聲明func時不使用類型別名,那麼久必須記住被定義的名字後面數組的維度:

int arr[10];             //arr是一個含有10個整數的數組
int *p1[10];             //p1是一個含有10個指針的數組
int (*p2)[10] = &arr;   //p2是一個指針,指向含有10個整數的數組

  類似的,如果定義一個返回數組指針的函數,那麼數組的維度必須跟在函數名字之後,且函數的形參列表也跟在函數摸那個字後面並位於數組維度之前,即:

Type (*function (parameter_list)) [dimension]
int (*func(int i))[10];

  ②使用尾置返回類型
  除上述簡化func聲明外,還可以使用尾置返回類型,其跟在形參列表後面並以一個->符號開頭,爲了表示函數真正的返回類型跟在形參列表之後,在本應該出現返回類型的地方放置一個auto:

//func接收一個int類型的實參,返回一個指針,該指針指向含有10個整數的數組
auto func(int i) -> int(*)[10];

  ③使用decltype
  如果知道函數返回的指針指向那個數組,可以適應decltype關鍵字返回類型:

int odd[] = {1,3,5,7};
int even[] = {0,2,4,6};
//返回一個指針,指向含有5個整數的數組
decltype(odd) *arrPtr(int i)
{
   return (i % 2) ? &odd:&even;   //返回一個指向數組的指針

二、函數重載
  函數重載指在同一作用域內名字相同但形參列表不同的函數。

2.1 定義重載函數
  對於重載的函數來說,它們的形參數量應該在數量或類型上有所不同,如果是一致的,則第二個函數的聲明時錯誤的。

Record lookup(const Account&);  //根據Account查找記錄
Record lookup(const Phone&);    //根據Phone查找記錄
Record lookup(const Name&);     //根據Name查找記錄
Account acct;
Phone phone;
Record r1 = lookup(acct);       //調用接收Account的版本
Record r2 = lookup(phone);      //調用接收phone的版本

2.2 重載和const形參
  因爲頂層const不影響傳入函數的對象,所以一個擁有頂層const的形參無法和另一個沒有頂層const的形參區分開來:

Record lookup(Phone);
Record lookup(const Phone);     //重複聲明瞭Record lookup(Phone)

  如果const是低層的,那麼可以實現函數重載。另外,因爲const不能轉換成其他類型,所以只能把const對象(或指向const的指針)傳遞給consti形參,而非常量可以轉換成const,所以下面的函數可以作用域非常量對象或指向非常量對象的指針,需要注意的是,當傳遞一個非常量對象或者指向非常量對象的指針時,編譯器會非常量版本的函數。

Record lookup(Account&);      //函數作用域Account的引用
Record lookup(const Account&);//新函數,作用域常量引用

2.3 const_cast和重載

const string &shorterString(const string &s1,const string &s2)
{
   return s1.size() <= s2.size() ? s1:s2;
}

  上述函數的參數和返回類型都是const string的引用,可以對兩個非常量的string實參調用這個函數,但返回結果仍然是const string的引用,可以使用const_cast得到一個普通的引用:

string &shorterString(const string &s1,const string &s2)
{
   auto &r = shorterString(const_cast<const string&>(s1),const_cast<const string&>(s2));
   return const_cast<string&>(r);
}

參考文獻:
①C++ primer 第五版

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章