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