返回類型和return語句


return語句終止當前正在執行的函數並將控制權返回到調用該函數的地方。return語句有兩種形式:

return;

return expression;

 

無返回值函數

沒有返回值的return語句只能用在返回類型是void的函數中。返回void的函數不要求非得有return語句,因為在這類函數的最后一句后面會隱含地執行return。

通常情況下,void函數如果想在它的中間位置提前退出,可以使用return語句。return的這種用法有點類似於我們用break語句退出循環。

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

 

有返回值的函數

return語句的第二種形式提供了函數的結果。只要函數的返回類型不是void,則該函數內的每天return語句必須返回一個值。return語句返回值的類型必須與函數的返回類型相同,或者能隱式地轉換成函數的返回類型。

盡管C++無法確保結果的正確性,但是可以保證每個return語句的結果類型正確。也許無法顧及所有情況,但是編譯器仍然盡量確保具有返回值的函數只能通過一條有效的return語句退出。

在含有return語句的循環后面應該也有一條return語句,如果沒有的話該程序就是錯誤的。

 

值是如何被返回的

返回一個值得方式和初始化一個變量或形參的方式完全一樣:返回的值用於初始化調用點的一個臨時量,該臨時量就是函數調用的結果。

必須注意當函數返回局部變時的初始化規則。例如我們寫一個函數,給定計數值、單詞和結束符之后,判斷計數值是否大於1:如果是,返回單詞的復數形式;如果不是,返回單詞原型:

//如果ctr的值大於1,返回word的復數形式

string make_plural(size_t ctr,const string &word,const string &ending)

{

  return (ctr>)?word+ending:word;

}

該函數的返回類型是string,意味着返回值將被拷貝到調用點。因此,該函數將返回word的副本或者一個未命名的臨時string對象,該對象的內容是word和ending的和。

同其他引用類型一樣,如果函數返回引用,則該引用僅是它所引對象的一個別名。舉個例子來說明,假定某函數跳出兩個string形參中較短的那個並返回其引用:

//挑出兩個string對象中較短的那個,返回其引用

const string &shorterString(const string &s1,const string &s2)

{

  return s1.size()<=s2.size()?s1:s2;

}

其中形參和返回類型都是const string的引用,不管是調用函數還是返回結果都不會真正拷貝string對象。

 

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

函數完成后,它所占的存儲空間也隨之被釋放掉。因此,函數終止意味着局部變量的引用將指向不再有效的內存區域:

//嚴重錯誤:這個函數試圖返回局部對象的引用

const string &manip()

{

  string ret;

  //以某種方式改變一下ret

  if(!ret.empty())

    return ret;  //錯誤:返回局部對象的引用

  else

    return "Empty";  //錯誤:“Empty”是一個局部臨時量

}

上面的兩條return語句都將返回未定義的值,也就是說試圖使用manip函數的返回值將引發未定義的行為。對於第一天return語句來說,顯然它返回的是局部對象的引用。在第二條return語句中,字符串字面值轉換成一個局部臨時string對象,對於manip來說,該對象和ret一樣都是局部的,當函數結束時臨時對象占用的空間也就隨之是否釋放了,所以兩條return語句都指向了不再可用的內存空間。

如前所述,返回局部對象的引用是錯誤的;同樣,返回局部對象的指針也是錯誤的。一旦函數完成,局部對象被釋放,指針將指向一個不存在的對象。

 

返回類類型的函數和調用運算符

和其他運算符一樣,調用運算符也有優先級和結合律。調用運算符的優先級與點運算符和箭頭運算符相同,並且也符合左結合律。因此,如果函數返回指針、引用或類的對象,我們就能使用調用的結果訪問結果對象的成員。

例如,我們可以通過如下形式得到較短string對象的長度:

//調用string對象的size成員,該string對象是由shorterString函數返回的

auto sz=shorterString(s1,s2).size();

因為上面提到的運算符都滿足左結合律,所以shorterString的結果是點運算符的左側運算對象,點運算符可以得到該string對象的size成員,size又是第二個調用運算符的左側運算對象。

 

引用返回左值

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

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';

  cout<<s<<endl;

  return 0;

}

把函數調用放在賦值語句的左側可能看起來有點奇怪,但其實這沒什么特別的。返回值是引用,因此調用是個左值,和其他左值一樣它也能出現在賦值運算符的左側。

如果返回類型是常量引用,我們不能給調用的結果賦值,這一點和我們熟悉的情況是一樣的:

shorterString("hi","bye")="X";   //錯誤:返回值是個常量

 

列表初始化返回值

C++11新標准規定,函數可以返回花括號包圍的值得列表。類似於其他返回結果,此處的列表也用來對表示函數返回的臨時量進行初始化。如果列表為空,臨時量執行值初始化;否則,返回的值由函數的返回類型決定。

 

返回數組指針

因為數組不能被拷貝,所以函數不能返回數組。不過,函數可以返回數組的指針或引用。雖然從語法上來說,要想定義一個返回數組的指針或引用的函數比較繁瑣,但是有一些方法可以簡化這一任務,其中最直接的方法是使用類型別名:

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]

類似於其他數組的聲明,Type表示元素的類型,dimension表示數組的大小。(*function(parameter_list))兩端的括號必須存在,就像我們定義p2是兩端必須有括號一樣。如果沒有這對括號,函數的返回類型將是指針的數組。

舉個具體點的例子,下面這個func函數的聲明沒有使用類型別名:

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

可以按照以下的順序來逐層理解該聲明的含義:

  • func(int i)表示調用func函數時需要一個int類型的實參;
  • (*func(int i))意味着我們可以對函數調用的結果執行解引用操作;
  • (*func(int i))[10]表示解引用func的調用將得到一個大小是10 的數組
  • int (*func(int i))[10]表示數組中的元素是int 類型

 

使用尾置返回類型

在C++11新標准中還有一種可以簡化上述func聲明的方法,就是使用尾置返回類型。然后函數的定義都能使用尾置返回,但是這種形式對於返回類型比較復雜的函數最有效,比如返回類型是數組的指針或者數組的引用。尾置返回類型跟在形參列表后面並以一個->符號開頭。為了表示函數真正的返回類型跟在形參列表之后,我們在本應該出現返回類型的地方放置一個auto:

//func接受一個int類型的實參,返回一個指針,該指針指向含有10個整數的數組

auto func(int i) ->int (*)[10];

因為我們把函數的返回類型放在了形參列表之后,所以可以清楚地看到func函數返回的是一個指針,並且該指針指向了含有10個整數的數組。

 

使用decltype

還有一種情況,如果我們知道函數返回的指針將指向哪個數組,就可以使用decltype關鍵字聲明返回類型。例如,下面的函數返回一個指針,該指針根據參數i的不同指向兩個已知數組中的某一個:

int odd[]={1,3,5,7,9};

int even[]={0,2,4,6,8};

//返回一個指針,該指針指向含有5個整數的數組

decltype(odd) *arrPtr(int i)

{

  return (i%2)?&odd:&even;  //返回一個指向數組的指針

}

arrPtr使用關鍵字decltype表示它的返回類型是個指針,並且該指針所指的對象與odd的類型一致。因為odd是數組,所以arrPtr返回一個指向含有5個整數的數組的指針。有一個地方需要注意:decltype並不負責把數組類型轉換成對應的指針,所以decltype的結果是個數組,要想表示arrPtr返回指針還必須在函數聲明時加一個*符號。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM