(1)C++中的 const 常量能夠替代宏常數定義,如:數據庫
const int A = 3; //等價於 #define A 3
(2)C++中是否有解決方案,能夠用來替代宏代碼片斷呢?函數
(1)C++編譯器能夠將一個函數進行內聯編譯,被 C++編譯器內聯編譯的函數叫內聯函數。優化
(2)C++中使用 inline 關鍵字聲明內聯函數。如spa
inline int func(int a, int b) { return a < b ? a : b; }
(3)內聯函數聲明時 inline 關鍵字必須和函數定義結合在一塊兒,不然編譯器會直接忽略內聯請求。(在 vs2013 下,inline 放在聲明或定義前都可以)指針
(1)C++編譯器直接將內聯函數的函數體插入到函數調用的地方調試
(2)內聯函數沒有普通函數調用時的額外開銷(壓棧、跳轉、返回)code
(3)C++中推薦使用內聯函數替代宏代碼片斷。blog
(4)C++編譯器也不必定知足函數的內聯請求。遞歸
#include <stdio.h> #define FUNC(a, b) ((a) < (b) ? (a) : (b)) //MSVC下:要讓inline、__forceinline生效必須得作以下的設置: //①在「項目」→「配置屬性」→「C / C++」 →「優化」→「內聯函數擴展」中選擇「只適用於__inline(/ Ob1)」 //②在「配置屬性」→「C / C++」 →「全部選項」→「調試信息格式」中選擇「程序數據庫( / Zi)」 //VS2013下,inline可放在聲明前或也可放在定義前。或二者前都加 inline int func(int a, int b) { return a < b ? a : b; } int main() { int a = 1; int b = 3; //int c = FUNC(++a, b);//至關於(++a)<(b)?:(++a):(b); //printf("a = %d\n", a); //3 //printf("b = %d\n", b); //3 //printf("c = %d\n", c); //3 int c = func(++a, b); printf("a = %d\n", a);//2 printf("b = %d\n", b);//3 printf("c = %d\n", c);//2 return 0; }
內聯函數沒嵌入到調用地方(仍爲函數調用)ci
函數體被嵌入到調用的地方
宏 |
內聯函數 |
|
處理方式 |
由預處理器處理,只是進行簡單的文本替換 |
由編譯器處理,會將函數體嵌入到調用的地方。但內聯請求也可能被編譯器拒絕 |
類型檢查 |
不作類型檢查 |
具備普通函數的特徵,會進行參數和返回類型的檢查。 |
反作用 |
有 |
無 |
(1)現代 C++編譯器可以進行編譯優化,一些函數即沒有 inline 聲明,也可能被內聯編譯。
(2)一些現代的 C++編譯器提供了擴展語法,可用下列列關鍵字替代 inline 來對函數進行強制內聯,如:
①g++:__atrribute__((always_inline)) ②MSVC:__forceinline
(3)MSVC 下:要讓 inline、__forceinline 生效必須得作以下的設置:
①在「項目」→「配置屬性」→「C/C++」 →「優化」→「內聯函數擴展」中選擇「只適用於__inline(/Ob1)」
②在「配置屬性」→「C/C++」 →「全部選項」→「調試信息格式」中選擇「程序數據庫(/Zi)」
#include <stdio.h> //MSVC2013下:在函數聲明或定義前加inline或__forceinline均可以 //同時,這兩個的表現行爲幾乎如出一轍。只不過__forceinline是MS //下的,而inline是標準C++的,可移植性更高。 //__forceinline //__attribute__((always_inline)) //inline int add_inline(int n); int main() { int r = add_inline(10); printf("r = %d\n", r); return 0; } __forceinline int add_inline(int n) { int ret = 0; for (int i = 0; i < n; i++) { ret += i; } return ret; }
(1)含有遞歸調用的函數不能設置爲 inline
(2)使用了複雜流程控制語句:循環語句和 switch 語句,沒法設置爲 inline(說明:如上述實例,在 VS2013 下,循環語句是能夠被內聯的)
(3)函數體不能過於龐大
(4)不能對函數進行取址操做
(5)函數內聯聲明必須在調用語句以前.
(1)C++中能夠在函數聲明時爲參數提供一個默認值(注意是聲明,不能在定義中提供)
(2)當函數調用時沒有提供參數的值,則使用默認值
默認參數值
#include <stdio.h> //默認值只能在函數聲明時提供 int mul(int x = 0); //參數x的默認值爲0 int main() { printf("%d\n", mul()); //傳入默認值0 printf("%d\n", mul(-1)); //傳入-1 printf("%d\n", mul(2)); //傳入2 return 0; } int mul(int x) //定義中,不能提供默認值,編譯器會報錯 { return x * x; }
(3)函數參數默認值的規則
①聲明時,默認值必須從右向左提供
②函數調用時,若是使用了默認值,則後續參數必須使用默認值。
#include <stdio.h> //默認參數必須從右向左提供,諸如 //int add(int x = 0,int y = 1,int z)是錯誤的 int add(int x, int y = 1, int z = 2); int main() { //第2參數y使用了默認值,則後續的z也必須使用默認值 //諸如add(1, ,3);的調用是錯的。 printf("%d\n", add(0)); //x = 0, y = 1, z = 2 printf("%d\n", add(2, 3)); //x = 2, y = 3, z = 2 printf("%d\n", add(3, 2, 1));//x = 3, y = 2, z = 1 return 0; } int add(int x, int y, int z) { return x + y + z; }
(1)佔位參數只有參數類型聲明,而沒有參數名聲明,如:int func(int x,int)
(2)通常狀況下,在函數體內部沒法使用佔位參數
(3)佔位參數的意義
①佔位參數與默認參數結合起來使用
②兼容 C 語言程序中可能出現的不規範寫法
C++中支持佔位參數,用於兼容 C 語言中的不規範寫法
佔位參數與默認參數值
#include <stdio.h> //在C中int func()是能夠接受任意參數的,因此在後來的調用中可能 //出現func(1)、func(2, 3)等不一樣的調用,而這樣的代碼在C++中是 //錯誤的,因此爲了兼容C語言這種不規範的寫法,能夠給func提供兩個 //佔用參數如func(int = 0,int = 0),則前面的兩種調用就合法了, //這樣花不多的代價,就可讓C的代碼能夠在C++中使用。讓人感受仿 //佛C++也能夠像C語言同樣,接受任意個參數了! //佔位參數,且默認值爲0 int func(int x = 0, int = 0); int main() { printf("%d\n", func()); //0 printf("%d\n", func(1)); //1 printf("%d\n", func(2, 3)); //2 return 0; } //第2個參數爲佔位參數(沒函數名),所以在函數內部也就沒法使用 //這個參數,只起到佔位的做用 int func(int x, int) { return x; }
(1)用同一個函數名定義不一樣的函數
(2)當函數名和不一樣的參數搭配時,函數的含義不一樣。
#include <stdio.h> #include <string.h> int func(int x) { return x; } int func(int a, int b) { return a + b; } int func(const char* s) { return strlen(s); } int main() { printf("%d\n", func(3)); //int (int) printf("%d\n", func(4,5)); //int (int,int) printf("%d\n", func("Hello World!")); //int (const char* s) return 0; }
(1)重載的條件:必須至少知足下面的一個條件
①參數個數不一樣
②參數類型不一樣
③參數順序不一樣
(2)函數重載的注意事項
①重載函數在本質上是相互獨立的不一樣函數。
②重載函數的函數類型不一樣
③函數的返回值不能做爲函數重載的依據
④函數重載是由函數名和參數列表共同決定的。
函數重載的本質
include <stdio.h> int add(int a, int b) //函數類型:int(int,int) { return a + b; } int add(int a, int b, int c) //函數類型:int(int, int, int) { return a + b + c; } int main() { //printf("%p\n", add);//由於函數的重載,在編譯的結果中找不到這樣的函數名 //如下兩個printf顯示出來,重載函數的本質是相互獨立的兩個函數,其函數地址 //是不一樣的。 printf("%p\n",(int (*)(int, int))add); //在add前面加上類型,編譯器就會 //就根據重載函數的命名規則找到 //被編譯後的真正的函數名 printf("%p\n",(int (*)(int, int,int))add);//在add前面加上類型,編譯器就會 //就根據重載函數的命名規則找到 //被編譯後的真正的函數名 return 0; }
(1)編譯器調用重載函數的準則
①將全部同名函數做爲候選者
②嘗試尋找可行的候選函數(注意,下面 3 種匹配任一種後,會繼續匹配下一種,因此可能出現多個匹配的結果!)
A.精確匹配實參;B 經過默認參數可以匹配實參;C 經過默認類型轉換匹配實參
③匹配失敗
A.最終尋找到的候選函數不惟一,則出現二義性,編譯失敗。
B.沒法匹配全部候選者,函數未定義,編譯失敗
函數默認參數 VS 函數重載
#include <stdio.h> int func(int a, int b, int c = 0) { return a * b * c; } int func(int a, int b) { return a + b; } int main() { //根據匹配原則:經過函數名找到兩個候選函數 //並嘗試先經過精確匹配會找到func(int,int) //但這時並不會中止匹配,而是會嘗試用默認參數去匹配 //因此會找到另外一個func,即func(int,int,int = 0),所以 //出現了二義性,編譯器直接報錯。 int c = func(1, 2); return 0; }
函數重載用於模擬天然語言中的詞彙搭配,使得 C++具備更豐富的語義表達能力。函數重載的本質爲相互獨立的不一樣函數,C++中經過函數名和函數參數肯定函數調用。
(1)將重載函數名賦值給函數指針時
①根據重載規則挑選與函數指針參數列表一致的候選者
②嚴格匹配候選者的函數類型與函數指針的函數類型(所謂嚴格匹配,即函數參數及返回值都匹配)
函數重載 VS 函數指針
#include <stdio.h> #include <string.h> int func(int x) { return x; } int func(int a, int b) { return a + b; } int func(const char* s) { return strlen(s); } //聲明函數指針 typedef int (*PFUNC)(int a); int main() { int c = 0; PFUNC p = func;//編譯器將根據函數指針的類型去嚴格匹配對應的函數 //因此會找到int func(int);其餘函數則匹配不成功 c = p(1); // printf("c = %d\n", c); //1 return 0; }
(2)注意事項
①函數重載必然發生在同一個做用域中(如,同一個類或同一命名空間中)
②編譯器須要用參數列表或函數類型進行函數選擇
③沒法直接經過函數名獲得重載函數的入口地址(由於編譯結束後,C++會根據重載函數命名的規則重命名各個函數,而原來的函數名其實是找不到的)
1)實際工做中 C++和 C 代碼相互調用是不可避免的
(2)C++編譯器可以兼容 C 語言的編譯方式
(3)C++編譯器會優先使用 C++編譯的方式
(4)extern 關鍵字能強制 C++編譯器進行 C 方式的編譯
C++調用 C 函數
//add.h int add(int a, int b);
//add.c #include "add.h" //該文件的編譯,獲得目標文件add.o //gcc -c add.c int add(int a, int b) { return a + b; }
//main.cpp #include <stdio.h> //該文件的編譯 //g++ main.cpp add.o #ifdef __cplusplus extern "C" { #endif //C++中以C的方式編譯:將add的函數名就是目標名 #include "add.h" #ifdef __cplusplus } #endif int main() { int c = add(1, 2); printf("c = %d\n", c); //3 return 0; }
(1)C++內置的標準宏:__cplusplus,能夠確保 C 代碼以統一的 C 方式編譯
#ifdef __cplusplus extern "C" { #endif ......; //C/C++代碼,將以C的方式編譯 #ifdef __cplusplus } #endif
C 調用 C++函數(其中的 C++函數己經被按 C 方式編譯)
//add.h //該文件的編譯,獲得目標文件add.o //g++ -c add.c #ifdef __cplusplus extern "C" { #endif //C++中以C的方式編譯:add的函數名就是目標名 int add(int a, int b); #ifdef __cplusplus } #endif
//add.cpp #include "add.h" //該文件的編譯,獲得目標文件add.o //g++ -c add.c #ifdef __cplusplus extern "C" { #endif //C++中以C的方式編譯:add的函數名就是目標名 int add(int a, int b) { return a + b; } #ifdef __cplusplus } #endif
//main.c #include <stdio.h> #include "add.h" //編譯方式: //gcc main.c add.o int main() { int c = add(1, 2); printf("c = %d\n", c); //3 return 0; }
C 調用 C++函數(其中的 C++函數是 C++方式編譯)
①假設別人提供了編譯好的 cpp 的頭文件和.o 目標文件,但其中的函數是以 C++方式編譯的,很明顯函數名是用 C++方式命名的。咱們的 C 文件裏不方便使用這個的函數名。
②解決的方案是:作一個 C++的封裝層,對其中的函數進行一個封裝,而後再用extern "c"編譯這些封裝層中的函數,最後就能夠在 C 文件中使用了。
//add.h int add(int a, int b);
//add.cpp #include "add.h" //編譯命令:g++ -c add.cpp int add(int a, int b) { return a + b; }
咱們的封裝層
//addEx.h int addEx(int a, int b);
//addEx.cpp #include "add.h" //編譯命令: //g++ -c addEx.cpp extern "C" int addEx(int a,int b) { return add(a, b); }
//main.c #include <stdio.h> #include "addEx.h" //編譯命令: //gcc main.c addEx.0 add.o int main() { int c = addEx(1, 2); printf("c = %d\n", c); //3 return 0; }
(2)注意事項
①C++編譯器不能以 C 的方式編譯重載函數,即若是在 extern "C"塊裏有兩個同名的函數裏,則會編譯失敗。
②編譯方式決定函數名被編譯後的目標名。C++編譯方式將函數名和參數列表編譯成目標名,而 C 編譯方式只將函數名做爲目標名進行編譯。