程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++的那些事:函數全解析

C++的那些事:函數全解析

編輯:C++入門知識

一、函數的結構

上面程序中,實參為a與b,但是在調用時,v1與v2接受的是a與b的副本,所以實際上a與b的值沒有變化。

2)指針形參

函數的形參可以是指針,此時將復制實參指針,其實這類跟1)原理類似,函數內並無法改變實參的指針值。只是函數可以通過復制到的地址改變實參指針所指向的值。

 swap(* v1, * temp = **v2 = **v1 = a = ,b =  *p1 = &a,*p2 = & 

上面程序中定義的swap的形參為指針類型,main中調用swap,實際上swap並不能改變p1與p2的值,只是改變了它們所指向的值。

3)const 形參

,因為本來函數就不會改變實參的值。像下面的定義,實際中編譯器會忽略const的定義,而將其視為int型。

 fcn(  i);

2,引用形參

1)在上面的程序中我們看到,如果想交換兩個變量的值,通過調用普通的非引用類型形參的函數,並不能實現。用它們的指針可以,同時我們也可以用引用。

 swap(& v1, & temp === a = ,b =  

在實際調用swap時,v1與v2實際相當於a與b的另一個名字。

2)在有的時候我們需要向函數傳遞大型對象,需要使用引用形參,如果直接使用復制實參的形式可以,但是它的效率太低了,甚至有些對象是無法復制的。但是使用引用形參時,我們不希望函數改變了實參傳入的值,我們就可以使用const來限定形參。下面程序用來判斷哪個字符串更長,明顯我們不希望函數會改變字符串的內容,我們就可以用const引用型的形參。

 isLonger(  &s1,   & s1.size() >

所以,如果使用引用形參的惟一的目的是避免復制實參時,則應將形參定義為const引用。

3)在使用引用形參函數時,有兩點值得注意:

不要用const限定的實參或字面值來調用非const引用形參函數。因為這樣函數內,可以改變實參的值,這不合法。

非const引用形參只能與完全同類型的非const對象關聯。

4)傳遞指向指針的引用

如下有下面的程序:

 swap(* &v1, * &* temp === a = ,b = * p1 = &a, *p2 = & 

上面的程序依然不能改變a與b的值,但是它改變了p1與p2的值,現在p1指向了b,而p2指向了a。

3,其他類型的形參

1)vector和其他類型的形參:一般在這種類型作為形參時,為了避免復制應該考慮形參聲明為引用類型。C++程序員傾向於傳遞容器中需要處理的元素的迭代器來傳遞容器。

2)數組形參:由於數組不能復制,所以不能直接編寫數組類型的形參函數,一般通過傳遞指向數組的元素的指針來處理數組。值得注意的是在通過引用傳遞數組時,在調用函數時形參與實參的類型要匹配。

 printValues( (&ar)[ i = , j[] = { ,  k[] = {,,,,,,,,,
    printValues(j);  
    printValues(k);   
     

二、函數的返回值

1)沒有返回值

很多函數並沒有返回值,尤其是現在C++風格,習慣於把需要的結果作為引用形參。這類型函數一般沒有return語句,有時候有return是使函數中途中斷執行。

2)返回非引用類型

這種情況在函數調用處,程序會用一個臨時變量復制函數的返回值。

3)返回引用

當函數返回引用類型時,並沒有復制返回值。相反,返回的是對象本身。

三、重載函數

出現在相同作用域中的兩個函數,如果具有相同的名字而形參不同,則稱為重載函數。

1)注意區分函數重載與重復聲明

有些看起來不同的形參,本質是相同的。下面代碼中的都是重復聲明的例子

typedef  func( func(newDouble i);  

 func1(,  = ); 
 func1( , func2( func2( );  

2)重載與作用域

局部聲明的函數,將屏蔽所有全局作用的同名函數。下面例子顯示,即使全局作用的函數更加匹配調用的實參類型,但是仍然調用的是局部的函數。

 print( print( 

上面程序中,將調用void print(double)函數,雖然42是int型。

3)重載確定的三個步驟

如果定義了眾多的函數重載,將存在函數調用到底與哪個重載函數相匹配的問題。我們通過下面的示例代碼來說明問題:

 f();  
 f();
 f();
 f(, );
 f(, );

第一步:確定候選函數

假如我們調用f(4.2),那麼先找到同名函數,並且在作用域內可見,上面例子中5個函數都滿足。

第二步:選擇可行的函數

必須滿足2個條件:一是函數形參與該調用實參個數相同;第二,每個實參的類型必須與對應的類型匹配,或者可以被隱式轉換為對應的形參類型。這裡我們再調用f(4.2)時,排除了1、4、5號函數,只剩下2與3。其中2號函數可以通過類型轉換來滿足。

第三步:尋找最佳匹配

在經過第二步確定後,剩下2與3函數,那麼2需要進行類型轉換,顯然3是最佳匹配了。

但是如果這樣調用f(42,4.2)。這時候就會出現二義性,編譯器將提示。

還有一種要注意的就是有默認參數的函數,比如我們定義6號函數為void f(double,int =1);那麼在調用f(4.2)時就會有二義性。

可基於函數的引用形參是指向const對象還是指向非const對象實現函數重載。

 isLonger(  &s1,   & s1.size() >

四、函數指針

1,如何定義一個指針為函數類型?我們知道一個函數的類型是由它的返回類型和形參類型共同決定,而與函數名無關。所以在定義一個指向函數的指針,必須包含形參表與返回值這些信息。

下面來看一個比較兩個字符串長度的函數:

 lengthCompare( &,  &);

那麼這個函數的類型即bool(const string&, const string&) 。如果要想定義一具指向這種類型函數的指針,則可以如下定義:

 (*pf) ( &,  &);

注意上式中*pf外面的括號不可以省略,不然pf就成了一個函數的定義,這個函數返回一個指向bool類型的指針。

2,當我們把函數名作為一個值使用時,該函數自動地轉換成指針,所以我們可以這樣對函數指針pf初始化。

pf == &lengthCompare;

上面兩種方法是等價的。

那麼在使用函數指針時,我們可以解引用,也可以不解引用。

 b1 = pf(, b2 = (*pf)(,);

3,在給函數指針賦值的時候,一定要注意函數類型的完全匹配,但是我們可以給指向任意函數類型的指針賦一個nullptr或值為0的整型常量表達式,表示該指針沒有指向任何一個函數。

4,如果定義了指向重載函數的指針,在使用這個指針時並不是根據形參來確定所調用的函數,而是根據指針的具體函數類型。即,編譯器根據指針類型決定選用哪個函數,指針類型必須與重載函數中的某一個精確匹配。

 ff(* ff( ff(unsigned  (*pf)(unsigned ) = ff; 
 num = 

5,函數指針作為另一個函數的形參。

有的時候,我們需要將一個函數作為一個參數傳遞給別一個參數,比如定義一個函數用來返回兩個對象中較大的那個,那麼我們需要將一個比較函數作為參數傳遞。

 & BigString( & s1,  & s2,  pf( & , & & BigString( & s1,  & s2,  (*pf)( & , &));

上面定義的函數原型顯得有點冗長,我們可以定義函數類型,來簡化上面的代碼:

typedef  Func( &,  &
typedef  (*Func)( &,  &* Func;

有了上面的定義,我們就可以簡單BigString的定義了:

 & BigString( & , , Func);

6,函數返回值為一個函數指針。

我們知道,函數並不能返回一個函數,但是可以返回一個指向函數的指針。

最簡單的方法,我們用類型別名定義一種函數類型:

 F = (*,); 
 PF = (*) (*, ); 

下面我們來定義返回函數指針的函數:

PF f(); 
F f(); 
F* f(); 

當然我們也可以直接定義f:

 (*f()) (*,);

由內向外觀察:首先f有一個形參表(int),所以f是一個函數,然後f的的左邊有一個*,說明f返回的是一個指針。進一步發現,指針的類型本身也包含形參表,因此指針指向函數,該函數的返回類型是int。

我們還可以用C++11中的尾置返回類型來聲明一個返回函數指針的函數:

auto f() ->  (*)(*, );

如果我們需要返回的函數類型有一個函數實例,那麼我們可以用decltype來說明函數的類型:

 func(*,* f(); 

注意上面代碼中decltype(func)返回的是一個函數類型,我們需要在後面加上*,說明一個函數指針類型。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved