日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++(13)--函数的进阶:内联、传递引用、参数默认值、重载、函数模板

發布時間:2023/12/13 c/c++ 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++(13)--函数的进阶:内联、传递引用、参数默认值、重载、函数模板 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

模塊化編程--函數的進階

  • 1.內聯函數
    • 1.1 inline基本情況
    • 1.2 inline 的前世今生-帶參的宏替換
  • 2.傳遞引用(重點)
    • 2.1引用、理由、注意事項
    • 2.3 交換兩個變量的數值
  • 3.返回引用
    • 3.1不要返回局部變量的引用
    • 3.2函數可以不返回值,默認返回傳入的引用對象本身
    • 3.3返回引用時,要求函數的參數中必須包含被返回的引用對象
  • 4.參數的默認值
  • 5.隨堂練習--使用函數實現游戲中的私聊
  • 6.函數重載(重點)
  • 7.函數模版(難點)

《老九學堂C++課程》《C++ primer》學習筆記。《老九學堂C++課程》詳情請到B站搜索《老九零基礎學編程C++入門》
-------------簡單的事情重復做,重復的事情用心做,用心的事情堅持做(老九君)---------------

1.內聯函數

1.1 inline基本情況

在大多數的機器上,調用函數都要做很多工作;調用前要先保存寄存器,并在返回時恢復;復制實參;程序還必須轉向一個新位置執行。在想利用函數的好處(代碼復用,修改效率高)但又想避免函數調用的時間資源消耗,就可以使用內聯函數。

內聯(inline)函數:(內部聯結)是C++為了提高程序運行速度所做的一項改進(C中沒有),與常規函數的主要區別在于被調用時的運行機制不同(編寫方式是一致的,在函數頭部多一個incline關鍵字)

常規函數運行機制:依據函數名找到函數具體實現處,執行函數
內斂函數運行機制:編譯器使用函數代碼替換函數調用

內聯函數:在函數聲明處/函數定義時,加關鍵字;二者取其一即可。

inline void func1(){cout << 1;cout << 2;} int main(){func1(); }

在編譯將展開函數func1():

int main(){cout << 1;cout << 2; }

如果 執行函數代碼的時間 比 處理函數調用的時間 長,則節約的時間只占整個過程的很小一部分。

建議函數較短的時候使用內聯,可以節省函數調用的時間。如果函數較長,將會耗費較多的內存空間(存代碼呀,后續之后可能會用在類中用到)

如果函數體太長的話,編譯器可能會選擇忽略inline關鍵字,轉而做普通函數調用

1.2 inline 的前世今生-帶參的宏替換

C時代,如何實現內聯呢?內斂等價于替換,C中常用的替換操作是宏定義

#include <iostream> #define N 5 // 以后在所有使用到N的場合都會被替換成5 #define S(num) num*num // 宏定義了一個S函數,參數是num, 以后在所有使用到S(num)的地方,都會被自動的體化成 num * num 還不需要考慮參數的數據類型,整型和浮點型都能傳,但是,直接替換會處問題 using namespace std;int main(){int result1 = S(5);float result2 = S(5.5);int result3 = S(5 + 10);cout << result1 << '\t' << result2 << '\t' << result3 << endl;return 0; }

C++ 中為了升級了帶參數宏定義直接替換的bug,但是也失去了內連函數的能傳任意類型數據的優勢

2.傳遞引用(重點)

2.1引用、理由、注意事項

引用:為對象起了另一名字,實際底層是對對象地址的封裝。引用并非對象,只是對象的一個別名。

引用更接近于const 指針,一旦于某個變量關聯起來,一般不會變換它效忠的對象;而是為這個對象做一些事情。

int value = 1024; int &refValue = value; // 引用必須初始化,使用refValue和value操作的是同一個對象 // 不可以直接引用常量(字面量) // double &d = 12.3; ? const double &d = 12.3; // ?? 因為之后對d的修改編譯器會報錯

使用引用的理由:

  • 可以更加簡便的書寫代碼,省掉指針傳參時的取地址符號
  • 可以直接傳遞某個對象,而不是把對象復制一份
  • 將引用變量作為參數時,函數對原始變量進行操作,而非原始變量的副本。
    但數據所占的內存比較大時,建議使用引用參數。

    注意:傳遞數組只能用指針,并使用const 關鍵字,

    2.3 交換兩個變量的數值

    demo 1: 交換兩個變量的值

    // 三個版本的交換兩個數字 void Swaps1(int, int); void Swaps2(int *, int *); void Swaps3(int &, int &);int main(){int num1 = 10, num2 = 5;Swaps1(num1, num2);cout << num1 << "\t" << num2 << endl;Swaps2(&num1, &num2);cout << num1 << "\t" << num2 << endl;Swaps3(num1, num2); cout << num1 << "\t" << num2 << endl;return 0;}void Swaps1(int num1, int num2){// 書寫簡單,功能不達int tmp = num1;num1 = num2;num2 = tmp; } void Swaps2(int *p1, int *p2){// 書寫繁雜,功能實現// 交換目標地址的值// 取p1目標值,給指針變量tmpint tmp = *p1;// 將p1目標值改為p2的目標值*p1 = *p2;// 將p2的目標值改為tmp中存的值*p2 = tmp; } void Swaps3(int &ref1, int &ref2){// 寫法和方式1一致,但是能起到交換的結果// 結合1和2 的優點int tmp = ref1;ref1 = ref2;ref2 = tmp; }

    使用引用的注意點:在不想修改變量的地方,需要使用cons限制

    demo2: 只想現實一下變量的值,而不想改造他們

    void show(const int &, const int &); int main(){int num1 = 10, num2 = 5;show(num1, num2);return 0; } void show(const int &ref1, const int &ref2){// ref1 = 998; 賦值編譯不過cout << ref1 << "\t" << ref2 << endl;}

    3.返回引用

    函數的返回值可以是引用類型

    3.1不要返回局部變量的引用

    (函數調用完,局部變量就被銷毀了,返回其引用是一個無意義的東西)

    void test(); int & Sum(); int & Sum(){// rnum是在Sum()函數中定義的局部變量,其生存期只在Sum()函數中int num = 10;int & rnum = num;return rnum;// 編譯: warning: reference to stack memory associated with local variable 'num' returned [-Wreturn-stack-address]// 內存回收:并不是把內存保存數值清零,而是沒有變量去操作他了,那一塊空間還是在} void test(){int x = 1; }

    輸出是對,是10

    int main(){// result 引用了一個局部變量int & result = Sum();cout << result; // 正常輸出return 0; }

    多加一個test輸出就不是10,相當于那一塊地址的值被1 占了,所以原來的result 引用就是后來的1了。
    (另一個函數將覆蓋先前函數棧上的數據)

    int main(){// result 引用了一個局部變量int & result = Sum();test(); // 多加一個測試函數就會變成1;cout << result;return 0; }

    3.2函數可以不返回值,默認返回傳入的引用對象本身

    編譯可以過,但是無法運行:
    zsh: illegal hardware instruction "/Users/chenyingying/CppProject/Helloworld/"main
    老師返回的是16,最后一個修改的參數

    int & Sum2(int &, int &);int main(){int num1 = 10;int num2 = 15;int &result = Sum2(num1, num2); // 編譯可以過,但是無法運行:// zsh: illegal hardware instruction "/Users/chenyingying/CppProject/Helloworld/"main// 老師返回的是16,最后一個修改的參數cout << "result = " << result << endl;return 0; } int & Sum2(int & num1, int & num2){num1++;num2++;// 編譯warming : control reaches end of non-void function [-Wreturn-type] }

    3.3返回引用時,要求函數的參數中必須包含被返回的引用對象

    (不能返回帶運算的引用return num1 + num2 是不對的)

    int & Sum2(int & num1, int & num2){num1++;num2++;// ?,提示:非常量引用的初始值必須為左值C/C++(461return num1 + num2; }

    在返回引用類型時,為了避免在設計過程中存在模糊的情況,在函數定義時增加const限定,規避由模糊引起的犯錯。

    4.參數的默認值

    參數可以有默認值,在聲明時可以傳遞默認值

    void sample(int = 10); int main(){sample();sample(123); } void sample(int num){cout << num << endl; }

    注意點:
    1.默認值可以在函數原型或者定義中給出,不能再兩個位置同時出現
    (實驗時在定義中給出,會報錯:main.cpp:16:6: note: candidate function not viable: requires 1 argument, but 0 were provided,我還是在聲明的時候給默認值吧)
    2.對于帶參數列表的函數,必須從右向左添加默認值(有默認值得得放在參數列表的后端)

    void test1(int a, int b = 5, int c = 10); // ??對的,調用時 test1(1); test1(1,2);test3(1,2,3);都對 void test2(int a = 2, int b = 5, int c); // ?錯,默認參數后的參數也必須有默認值 void test3(int a = 1, int b = 5, int c = 10); // ??對的,調用時 test3(1); test3(1,2);test3(1,2,3);都對

    5.隨堂練習–使用函數實現游戲中的私聊

    // 模擬現實游戲中的私聊函數 string chatTo(const string &toName, const string &content); string chatFrom(const string & = "系統", const string & = "快睡覺"); /*** 跟某人聊天--負責字符串的拼接(聊天格式)* Param tonName 聊天對象的名稱* Param content 聊天的內容* return 按某格式拼接聊天信息后的字符串 */ string chatTo(const string &toName, const string &content){string msg = "?? 你悄悄的對[" + toName + "]說:" + content;return msg; } string chatFrom(const string &fromName, const string &content){string msg = "??[" + fromName + "]悄悄對你說:" + content;return msg; }int main(){cout << "請輸入對方的名稱:";string toName,content,chatMsg;getline(cin, toName);cout << "請輸入發送對方的聊天信息:";getline(cin, content);chatMsg = chatTo(toName, content); // 后臺應該是需要做處理的cout << endl << chatMsg << endl;cout << "請輸入來自對方的信息:"; // 應該是從網絡上接收對的,但是這里只是模擬getline(cin, content);string fromName = toName;chatMsg = chatFrom(fromName, content); // 不能通過等號的方式進行傳參么cout << endl << chatMsg << endl;return 0; }

    6.函數重載(重點)

    overloading: 可以有多個同名函數, 但是參數列表不同(特征標不同)

    重載決議:編譯器在編譯時,會依據參數列表對函數進行重命名,來實現重載函數的唯一性

    (返回值不同并產生不同的函數重載)

    void Swap(int a, int b) 編譯器處理為 Swap_int_int() void Swap(float a, float b) 編譯器處理為 Swap_float_float()

    deno1: 類型的引用和類型本身所表達的參數列表是一致的(不是函數重載)

    void eating(string food){cout << "吃" << food << "1" << endl; } void eating(string &food){cout << "吃" << food << "2" << endl; }

    從編譯器的角度來看,eating(sting) 和eating(&string)的特征標(參數列表)是一致的

    還需要注意:函數重載時不區分const和非const變量。

    demo2: 使用重載實現不同數據類型數組的排序

    void sort(int[], int); void sort(float[], int); void sort(double[], int); void Show(int[], int); void Show(float[], int); void Show(double[], int);void sort(int nums[], int len){int tmp;// cout << "sizeof(nums):" << sizeof(nums) << endl;for(int i = 0; i < len; i++){for(int j = 0; j < len - i -1; j++){if(nums[j] > nums[j + 1]){tmp = nums[j];nums[j] = nums[j+1];nums[j+1] = tmp;}}}} void sort(float nums[], int len){float tmp;for(int i = 0; i < len; i++){for(int j = 0; j < len - i -1; j++){if(nums[j] > nums[j + 1]){tmp = nums[j];nums[j] = nums[j+1];nums[j+1] = tmp;}}}}void sort(double nums[], int len){double tmp;for(int i = 0; i < len; i++){for(int j = 0; j < len - i -1; j++){if(nums[j] > nums[j + 1]){tmp = nums[j];nums[j] = nums[j+1];nums[j+1] = tmp;}}}}void Show(int nums[], int len){for(int i=0; i < len; i++){cout << nums[i] << "\t" ;}cout << endl; } void Show(float nums[], int len){for(int i=0; i < len; i++){cout << nums[i] << "\t" ;}cout << endl; } void Show(double nums[], int len){for(int i=0; i < len; i++){cout << nums[i] << "\t" ;}cout << endl; }int main(){int iNums[] = {1, 3, 2, 4, 6, 5};float fNums[] = {1.2, 3.4, 7.8, 5.5, 6.9};double dNums[] = {1.2, 3.4, 7.8, 5.5, 6.9};cout << "排序前:";Show(iNums, sizeof(iNums)/sizeof(iNums[0]));sort(iNums, sizeof(iNums)/sizeof(int));cout << "排序后:";Show(iNums, sizeof(iNums)/sizeof(iNums[0]));cout << "排序前:";Show(fNums, sizeof(fNums)/sizeof(fNums[0]));sort(fNums, sizeof(fNums)/sizeof(float));cout << "排序后:";Show(fNums, sizeof(fNums)/sizeof(float));cout << "排序前:";Show(dNums, sizeof(dNums)/sizeof(dNums[0]));sort(dNums, sizeof(dNums)/sizeof(double));cout << "排序后:";Show(dNums, sizeof(dNums)/sizeof(double));return 0; }

    輸出

    排序前:1 3 2 4 6 5 排序后:1 2 3 4 5 6 排序前:1.2 3.4 7.8 5.5 6.9 排序后:1.2 3.4 5.5 6.9 7.8 排序前:1.2 3.4 7.8 5.5 6.9 排序后:1.2 3.4 5.5 6.9 7.8

    7.函數模版(難點)

    模板函數實際上就是建立一個通用函數,在該函數的定義時不指定具體的數據類型(使用虛擬類型代替)。模板函數被調用時,編譯器會根據實參反推數據類型(類型參數化)

    // 模板頭與函數聲明定義需要寫在一起 template <typename 類型參數1typename 類型參數2> 返回值類型 函數名(形參列表){// 在函數體重可以使用類型參數 }template<typename T> void Swap(T &, T &); // T 可以為任意類型, 可以看成類型參數化, typename(新推薦)/class(舊習慣) template<typename T> // 模版頭部 void Swap(T &a, T &b){T temp = a;a = b;b = temp; }

    demo1: 模版函數實現-函數重載(part 6 中demo2)

    template<typename T> void Sort(T tArray[], int len); template<typename T> void Show(T tArray[], int len);template<typename T> void Sort(T tArray[], int len){// 冒泡排序法T temp;for(int i = 0; i < len-1; i++){for(int j =0; j <len - i ; j++){if(tArray[j+1] < tArray[j]){temp = tArray[j];tArray[j] = tArray[i];tArray[i] = temp;}}} }template<typename T> void Show(T tArray[], int len){for(int i = 0; i < len; i++){cout << tArray[i] << "\t";}cout << endl; } int main(){//模版函數demoint iNums[] = {1, 3, 2, 4, 6, 5};float fNums[] = {1.2, 3.4, 7.8, 5.5, 6.9};double dNums[] = {1.2, 3.4, 7.8, 5.5, 6.9};cout << "排序前:";Show(iNums, sizeof(iNums)/sizeof(iNums[0]));Sort(iNums, sizeof(iNums)/sizeof(int));cout << "排序后:";Show(iNums, sizeof(iNums)/sizeof(iNums[0]));cout << "排序前:";Show(fNums, sizeof(fNums)/sizeof(fNums[0]));Sort(fNums, sizeof(fNums)/sizeof(float));cout << "排序后:";Show(fNums, sizeof(fNums)/sizeof(float));cout << "排序前:";Show(dNums, sizeof(dNums)/sizeof(dNums[0]));Sort(dNums, sizeof(dNums)/sizeof(double));cout << "排序后:";Show(dNums, sizeof(dNums)/sizeof(double));return 0; }

    輸出:

    排序前:1 3 2 4 6 5 排序后:1 2 4 6 3 5 排序前:1.2 3.4 7.8 5.5 6.9 排序后:5.5 1.2 6.9 3.4 7.8 排序前:1.2 3.4 7.8 5.5 6.9 排序后:5.5 1.2 6.9 3.4 7.8

    函數重載的優秀之處:相同的函數名可以擁有不同的行為(每個人的吃飯的方式都是存在差異的)
    函數模版的優秀之處:重載函數實現的邏輯都一致時,可以直接使用函數模版進行精簡。(中等級別的代碼用函數模版的還是比較少,在書寫框架的時候,函數模版就十分有用)

    兩者并非替代關系,當重載函數中的實現邏輯是一致時,才能用函數模版替代精簡代碼

    總結

    以上是生活随笔為你收集整理的C++(13)--函数的进阶:内联、传递引用、参数默认值、重载、函数模板的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。