C++基础03-C++对c的拓展-函数
一、內(nèi)聯(lián)函數(shù)
c 語言中有宏函數(shù)的概念。宏函數(shù)的特點是內(nèi)嵌到調(diào)用代碼中去,避免了函數(shù)調(diào)用 的開銷。但是由于宏函數(shù)的處理發(fā)生在預處理階段,缺失了語法檢測 和有可能帶來的語 意差錯。
特點:
???? 1)內(nèi)聯(lián)函數(shù)聲明時inline關(guān)鍵字必須和函數(shù)定義結(jié)合在一起,否則編譯器會直接忽略內(nèi)聯(lián)請求。
???? 2)C++編譯器直接將函數(shù)體插入在函數(shù)調(diào)用的地方 。
???? 3)內(nèi)聯(lián)函數(shù)沒有普通函數(shù)調(diào)用時的額外開銷(壓棧,跳轉(zhuǎn),返回)。
???? 4)內(nèi)聯(lián)函數(shù)是一種特殊的函數(shù),具有普通函數(shù)的特征(參數(shù)檢查,返回類型等)。
???? 5) 內(nèi)聯(lián)函數(shù)由 編譯器處理,直接將編譯后的函數(shù)體插入調(diào)用的地方,宏代碼片段 由預處理器處理, 進行簡單的文本替換,沒有任何編譯過程。
???? 6)C++中內(nèi)聯(lián)編譯的限制:
??????????? 不能存在任何形式的循環(huán)語句
??????????? 不能存在過多的條件判斷語句
??????????? 函數(shù)體不能過于龐大
??????????? 不能對函數(shù)進行取址操作
??????????? 函數(shù)內(nèi)聯(lián)聲明必須在調(diào)用語句之前
??? 7)編譯器對于內(nèi)聯(lián)函數(shù)的限制并不是絕對的,內(nèi)聯(lián)函數(shù)相對于普通函數(shù)的優(yōu)勢只是省去了函數(shù)調(diào)用時壓棧,跳轉(zhuǎn)和返回的開銷。因此,當函數(shù)體的執(zhí)行開 銷遠大于壓棧,跳轉(zhuǎn)和返回所用的開銷時,那么內(nèi)聯(lián)將無意義。
內(nèi)聯(lián)函數(shù)總結(jié):
??????? 優(yōu)點:避免調(diào)用時的額外開銷(入棧與出棧操作)
??????? 代價:由于內(nèi)聯(lián)函數(shù)的函數(shù)體在代碼段中會出現(xiàn)多個“副本”,因此會增加代碼段的空間。
??????? 本質(zhì):以犧牲代碼段空間為代價,提高程序的運行時間的效率。
??????? 適用場景:函數(shù)體很“小”,且被“頻繁”調(diào)用。
#if 0 #include<iostream> using namespace std;//代碼比較少時才采用inline函數(shù) //若代碼比較多,有復雜業(yè)務時即使申明為inline函數(shù),也不會采用inline函數(shù)的執(zhí)行方式 void PrintAB1(int a, int b) {cout << "a=" << a << endl;cout << "b=" << b << endl; } void test01() {int a = 10;int b = 10;for (int i = 0; i < 1000; i++){a++;b++;PrintAB1(a, b); //函數(shù)壓棧 a,b壓棧,形參拷貝實參,執(zhí)行語句,返回值再出棧 有一定的空間開銷} }//宏函數(shù) 解決了函數(shù)壓棧 出棧 但解決不了參數(shù)的替換問題(簡單的參數(shù)拷貝) #define MAX(a,b) \(a)>(b)?(a):(b)int max(int a, int b) {return (a > b) ? a : b; }void test02() {int a = 20;int b = 10;int c = 0;c = MAX(a, b);cout << c << endl; //20c = MAX(a++, b++); //(a++)>(b++)?(a++):(b++) 宏函數(shù)沒有語法檢測能力,預處理時執(zhí)行cout << c << endl; //21c = max(a++, b++);cout << c << endl; //22 }inline void PrintAB2(int a, int b) {cout << "a=" << a << endl;cout << "b=" << b << endl; }void test03() {int a = 10;int b = 10;for (int i = 0; i < 1000; i++){a++;b++;PrintAB2(a, b); //相當于將函數(shù)展開} } inline void PrintAB3(int a, int b); //在申明是使用inline,但是函數(shù)體沒有加inline 還是普通函數(shù)void PrintAB3(int a, int b){cout << "a=" << a << endl;cout << "b=" << b << endl; }int main() {test02(); } #endif二、函數(shù)的默認參數(shù)和占位參數(shù)
1、默認參數(shù)
通常情況下,函數(shù)在調(diào)用時,形參從實參那里取得值。對于多次調(diào)用用一函數(shù)同一 實參時,C++給出了更簡單的處理辦法。給形參以默認值,這樣就不用從 實參那里取值了。
在默認參數(shù)規(guī)則 ,如果默認參數(shù)出現(xiàn),那么右邊的都必須有默認參數(shù)? 存在默認參數(shù)時, 當置于右側(cè)。
默認參數(shù)規(guī)則:
???????? 只有參數(shù)列表后面部分的參數(shù)才可以提供默認參數(shù)值
??????? 一旦在一個函數(shù)調(diào)用中開始使用默認參數(shù)值,那么這個參數(shù)后的所有參 數(shù)都必須使用默認參數(shù)值
2、占位參數(shù)
??????? 函數(shù)占位參數(shù)
?????? 占位參數(shù)只有參數(shù)類型聲明,?沒有參數(shù)名聲明
?????? ?般情況下,在函數(shù)體內(nèi)部?法使?占位參數(shù)
#include<iostream> using namespace std;//1、默認參數(shù) void func1(int a) {cout << "a=" << a << endl; }void func2(int a = 666) {cout << "a=" << a<<endl; } void test01() {int a = 200;func1(a); //200//func1(); //編譯錯誤func2(a); //200func2(); //正確 666 } int get_volume1(int len, int width, int height) {cout << "len=" << len << endl;cout << "width=" << width << endl;cout << "height=" << height << endl;return len*width*height; } int get_volume2(int len, int width, int height=1) { //左邊有默認值時,右邊必須有默認值cout << "len=" << len << endl;cout << "width=" << width << endl;cout << "height=" << height << endl;return len*width*height; } void test02() {int len = 10;int w = 20;int h = 30;cout << "體積是:"<<get_volume1(len, w, h) << endl; //6000cout << "體積是:" << get_volume2(len, w) << endl; //200 從左向右對應參數(shù) }//2、占位參數(shù) void funn1(int x) {cout << "x=" << x << endl; } void funn2(int x, int); void funn2(int x,int) { //第二個參數(shù)無意義,但必須傳遞值 cout << "x=" << x << endl; } void funn3(int x, int = 0) { //第二個參數(shù)無意義,但必須傳遞值 cout << "x=" << x << endl; } void test03() {int a = 10;funn1(a);funn2(10, 20);//funn2(10); //錯誤funn3(10, 20);funn3(10); //正確 } int main() {//test01();test02(); }三、函數(shù)重載
1、函數(shù)重載(Function Overload):用同一個函數(shù)名定義不同的函數(shù),當函數(shù)名和不同的參數(shù)搭配時函數(shù)的含義不同
2、重載規(guī)則(參數(shù)個數(shù)不同 參數(shù)類型不同 參數(shù)順序不同):(前提是在同一作用域之內(nèi))
?????? ? 1,函數(shù)名相同。
???????? 2,參數(shù)個數(shù)不同,參數(shù)的類型不同,參數(shù)順序不同,均可構(gòu)成重載。
???????? 3,返回值類型不同則不可以構(gòu)成重載。 僅返回值類型不同,不是重載。
3、調(diào)用規(guī)則
??????? 1,嚴格匹配,找到則調(diào)用。
??????? 2,通過隱式轉(zhuǎn)換尋求一個匹配,找到則調(diào)用。
4、編譯器調(diào)用重載函數(shù)的準則:
??????? 1.將所有同名函數(shù)作為候選者
??????? 2.嘗試尋找可行的候選函數(shù)
??????? 3.精確匹配實參
??????? 4.通過默認參數(shù)能夠匹配實參
??????? 5.通過默認類型轉(zhuǎn)換匹配實參
??????? 6.匹配失敗
??????? 7.最終尋找到的可行候選函數(shù)不唯一,則出現(xiàn)二義性,編譯失敗。
??????? 8.無法匹配所有候選者,函數(shù)未定義,編譯失敗。
5、重載底層實現(xiàn)(name mangling)
?????? C++利用 name mangling(傾軋)技術(shù),來改名函數(shù)名,區(qū)分參數(shù)不同的同名函數(shù)。 實現(xiàn)原理:用 v c i f l d 表示 void char int float long double 及其引 用。
?????? void func(char a); // func_c(char a)
?????? void func(char a, int b, double c); //func_cid(char a, int b, double c)
6、函數(shù)重載與函數(shù)默認參數(shù)
?????? 一個函數(shù),不能既作重載,又作默認參數(shù)的函數(shù)。當你少寫一個參數(shù)時,系統(tǒng)無法確認是重載還是默認參數(shù)。
#if 1 #include<iostream> using namespace std;//函數(shù)的返回值 函數(shù)的形參列表(參數(shù)個數(shù),參數(shù)類型,參數(shù)順序) /* int func(int a, int b) {//... } */ //c語言中只要函數(shù)名相同 無法通過編譯 //1、c++中增加了函數(shù)重載:函數(shù)名相同,形參列表不同 //若函數(shù)名相同,形參列表相同,函數(shù)返回值不同,則不可以通過編譯 //2、函數(shù)返回值并不是構(gòu)成函數(shù)重載的條件 //3、如果有函數(shù)重載,不要寫默認參數(shù),為了避免調(diào)用出現(xiàn)函數(shù)沖突//調(diào)用規(guī)則 // <1>如果能夠嚴格匹配調(diào)用完全匹配 // <2>如果沒有完全匹配,調(diào)用隱式轉(zhuǎn)換 // <3>如果都匹配不到,調(diào)用失敗 int func1(int a, int b) {cout << "func1(int a, int b) " << endl;return 0; } int func1(int a, char b) {cout << "func1(int a, char b)" << endl;return 0; } /* void func1(int a, char b) {cout << "func2" << endl; } */ void test01() {func1(1, 2); //func1(int a, int b)func1(1, 'a'); //func1(int a, char b) }int func2(int a, char b) {cout << "func2(int a, char b)" << endl;return 0; } int func2(int a, char b, int c = 0) { //等價于int func2(int a, char b, int=0) {cout << "func2(int a, char b, int c = 0)" << endl;return 0; } //如果有函數(shù)重載,不要寫默認參數(shù),為了避免調(diào)用出現(xiàn)函數(shù)沖突 void test02() {//func2(1, 'a'); 編譯錯誤 編譯器不會選擇func2(1, 'a', 22); }int func3(int a, char b) {cout << "func3(int a, char b)" << endl;return 0; } int func3(int a, char b, int c) { //等價于int func3(int a, char b, int) {cout << "func3(int a, char b, int c)" << endl;return 0; }void test03() {func3(1, 'a'); func3(1, 'a', 22); }void print1(int a) {cout << "print1(int a)" << " a=" << a << endl; } void print1(double a) {cout << "print1(double a)" <<" a=" << a << endl; } /* void print1(char a) {cout << "print1(char a)" << " a=" << a << endl; }*/ void test04() {print1(10); //print1(int a) a = 10print1(1.2); //print1(double a) a = 1.2print1('A'); //print1(int a) a = 65print1(3.1f); //print1(double a) a=3.1 隱式轉(zhuǎn)換//print1("ter"); 報錯沒有匹配 } int main() {//test01();//test02();//test03();test04();return 0; } #endif7、函數(shù)重載和函數(shù)指針結(jié)合
???????????? 函數(shù)重載與函數(shù)指針
???????????? 當使?重載函數(shù)名對函數(shù)指針進?賦值時
???????????? 根據(jù)重載規(guī)則挑選與函數(shù)指針參數(shù)列表?致的候選者
???????????? 嚴格匹配候選者的函數(shù)類型與函數(shù)指針的函數(shù)類型
8、函數(shù)指針基本語法
?????? //?法?:
????? //聲明?個函數(shù)類型
????? typedef void (myTypeFunc)(int a,int b);
????? //定義?個函數(shù)指針
????? myTypeFunc *myfuncp = NULL; //定義?個函數(shù)指針 這個指針指向函數(shù)的??地址
????? //?法?:
????? //聲明?個函數(shù)指針類型
????? typedef void (*myPTypeFunc)(int a,int b) ; //聲明了?個指針的數(shù)據(jù)類型
???? //定義?個函數(shù)指針
????? myPTypeFunc fp = NULL; //通過 函數(shù)指針類型 定義了 ?個函數(shù)指針 ,
????? //?法三:
????? //定義?個函數(shù)指針 變量
????? void (*myVarPFunc)(int a, int b);
#include<iostream> using namespace std;int func(int a, int b) {cout << "func" << endl;return 0; }//1、定義一種函數(shù)類型 typedef int(MY_FUNC)(int, int); //第一個int為返回值類型 //(int, int)為參數(shù)列表 //MY_FUNC為函數(shù)類型名 void test01() {//1.MY_FUNC *fp = NULL;fp = func;fp(10, 20); //func(*fp)(10, 20); //func }//2、定義一個指向函數(shù)類型的指針類型 typedef int(*MY_FUNC_P)(int, int); //MY_FUNC_P為指向int func(int a, int b) 函數(shù)類型的指針類型名 void test02() {MY_FUNC_P fp = NULL;fp = func;fp(10, 20); //func }//3、通過函數(shù)類型直接定義 常用 void test03() {int(*fp3)(int, int) = NULL;fp3 = func;fp3(100, 200); //func } int funcc(int a, int b) {cout << "funcc(int a, int b)" << endl;return 0; } int funcc(int a, int b,int c) {cout << "funcc(int a, int b,int c)" << endl;return 0; } void test04() {int(*fp3)(int, int) = NULL;fp3 = funcc; //fp3-->funcc(int a, int b)fp3(100, 200); //funcc(int a, int b)//fp3(10, 20, 30); 編譯錯誤 函數(shù)重載和函數(shù)指針重載是兩回事 }//實際上在給函數(shù)指針賦值的時候 是會發(fā)生函數(shù)重載匹配的 //在調(diào)用函數(shù)指針時,所調(diào)用的函數(shù)就已經(jīng)是固定的了 int main() {//test01();//test02();//test03();test04();return 0; }函數(shù)重載總結(jié):
??????? 重載函數(shù)在本質(zhì)上是相互獨立的不同函數(shù)。
?????? 函數(shù)的函數(shù)類型是不同的
?????? 函數(shù)返回值不能作為函數(shù)重載的依據(jù)
?????? 函數(shù)重載是由函數(shù)名和參數(shù)列表決定的。
總結(jié)
以上是生活随笔為你收集整理的C++基础03-C++对c的拓展-函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: github下载
- 下一篇: C++基础04-类基础