函式在c++中的使用,無非2種地方,一處是函式的定義,一處是函式的呼叫。而函式的定義則非常簡單,由三個部分組成:函式的返回型別、函式名和函式的形參表。當然,這裡不同的函式定義可以還會稍有不同,比如類的成員函式、內聯函式等。這裡我們主要討論函式的呼叫時需要注意的一些問題。
一、引數傳遞
我們將函式定義或宣告裡的引數叫形參,而在呼叫函式時傳入的引數叫實參。那麼根據形參型別的不同,有幾下形式的引數傳遞。
1,非引用形參
1)普通的內建型別
普通非引用型別的引數通過複製對應的實參實現形參的初始化。當用實參的副本初始化形參時,函式並沒有訪問呼叫所傳遞的實參的本身,因此函式不可能改實參的值。比如下面的交換兩個數的程式:
**如下:
void swap(int v1, int v2)
swap(a, b);// 呼叫swap
上面程式中,實參為a與b,但是在呼叫時,v1與v2接受的是a與b的副本,所以實際上a與b的值沒有變化。
2)指標形參
函式的形參可以是指標,此時將複製實參指標,其實這類跟1)原理類似,函式內並無法改變實參的指標值。只是函式可以通過複製到的位址改變實參指標所指向的值。
**如下:
void swap(int* v1, int* v2)
int main()
上面程式中定義的swap的形參為指標型別,main中呼叫swap,實際上swap並不能改變p1與p2的值,只是改變了它們所指向的值。
3)const 形參
對於普通的非引用型別用const修飾實際上是沒有意義的,因為本來函式就不會改變實參的值。像下面的定義,實際中編譯器會忽略const的定義,而將其視為int型。
**如下:
void fcn(const int i);
2,引用形參
1)在上面的程式中我們看到,如果想交換兩個變數的值,通過呼叫普通的非引用型別形參的函式,並不能實現。用它們的指標可以,同時我們也可以用引用。
**如下:
void swap(int& v1, int& v2)
int main()
在實際呼叫swap時,v1與v2實際相當於a與b的另乙個名字。
2)在有的時候我們需要向函式傳遞大型物件,需要使用引用形參,如果直接使用複製實參的形式可以,但是它的效率太低了,甚至有些物件是無法複製的。但是使用引用形參時,我們不希望函式改變了實參傳入的值,我們就可以使用const來限定形參。下面程式用來判斷哪個字串更長,明顯我們不希望函式會改變字串的內容,我們就可以用const引用型的形參。
**如下:
bool islonger(const string &s1, const string &s2)
所以,如果使用引用形參的惟一的目的是避免複製實參時,則應將形參定義為const引用。
3)在使用引用形參函式時,有兩點值得注意:
不要用const限定的實參或字面值來呼叫非const引用形參函式。因為這樣函式內,可以改變實參的值,這不合法。
非const引用形參只能與完全同型別的非const物件關聯。
4)傳遞指向指標的引用
如下有下面的程式:
**如下:
void swap(int* &v1, int* &v2)
int main()
上面的程式依然不能改變a與b的值,但是它改變了p1與p2的值,現在p1指向了b,而p2指向了a。
3,其他型別的形參
1)vector和其他型別的形參:一般在這種型別作為形參時,為了避免複製應該考慮形參宣告為引用型別。c++程式設計師傾向於傳遞容器中需要處理的元素的迭代器來傳遞容器。
2)陣列形參:由於陣列不能複製,所以不能直接編寫陣列型別的形參函式,一般通過傳遞指向陣列的元素的指標來處理陣列。值得注意的是在通過引用傳遞陣列時,在呼叫函式時形參與實參的型別要匹配。
**如下:
void printvalues(int (&ar)[10]);
int main()
;int k[10] = ;
printvalues(i); //error int不能初始化 int(&)[10]
printvalues(j); //error int[2] 不能初始化 int(&)[10]
printvalues(k); // ok
return 0;
}二、函式的返回值
1)沒有返回值
很多函式並沒有返回值,尤其是現在c++風格,習慣於把需要的結果作為引用形參。這型別函式一般沒有return語句,有時候有return是使函式中途中斷執行。
2)返回非引用型別
這種情況在函式呼叫處,程式會用乙個臨時變數複製函式的返回值。
3)返回引用
當函式返回引用型別時,並沒有複製返回值。相反,返回的是物件本身。
在返回引用這種情況下,注意不要返回區域性變數的引用,因為區域性變數在函式體內定義,當函式執行完後就銷毀了,所謂的引用也就沒有意義了。同理,不要返回指向區域性變數的指標。
三、過載函式
出現在相同作用域中的兩個函式,如果具有相同的名字而形參不同,則稱為過載函式。
1)注意區分函式過載與重複宣告
有些看起來不同的形參,本質是相同的。下面**中的都是重複宣告的例子
**如下:
typedef double newdouble;
int func(double i);
int func(newdouble i); // 沒有新型別
int func1(int, int = 1); //只是提供預設引數
int func1(int ,int);
int func2(int);
int func2(const int); //對於普通非引用形參用cosnt修飾是沒有意義的
2)過載與作用域
區域性宣告的函式,將遮蔽所有全域性作用的同名函式。下面例子顯示,即使全域性作用的函式更加匹配呼叫的實參型別,但是仍然呼叫的是區域性的函式。
**如下:
void print(int);
int main()
上面程式中,將呼叫void print(double)函式,雖然42是int型。
3)過載確定的三個步驟
如果定義了眾多的函式過載,將存在函式呼叫到底與哪個過載函式相匹配的問題。我們通過下面的示例**來說明問題:
**如下:
void f(); // 1
void f(int);// 2
void f(double);// 3
void f(int, int);// 4
void f(double, double);// 5
第一步:確定候選函式
假如我們呼叫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物件實現函式過載。
from:
ORACLE常用函式的使用方法
oracle常用函式的使用方法 1.字串函式 1 length 獲取字元長度 select length 中國 from platform metainfo tables where table name xmbm 2 lengthb 獲取位元組長度 select lengthb 中國 from p...
Juqery 常用函式使用方法
1.文件載入完成執行函式 document ready function 2.新增 刪除css類 some id addclass newclassname some id removeclass classnametoberemoved 3.選擇符 利用了css和xpath xml path la...
git的使用方法小結
git 是乙個開源的分布式版本控制工具,它的開發者就是鼎鼎大名的linux 作業系統的作者linus torvalds。git 被開發出來的初衷本是為了更好地管理linux 核心,而現在卻早已被廣泛應用於全球各種大中小型的專案中。今天是我們關於git 的第一堂課,主要是講解一下它最基本的用法,那麼就...