c語言入門到c++使用高手:深入學習C++之基本語言(六)

第二章 基本語言

第六節 函數新特性、內聯函數、const詳解

1. 函數回顧與後置返回類型

  • 函數定義中,形參如果在函數體內用不到的話,則可以不給形參變量的名字,只給其變量

  • 函數聲明又叫函數原型

  • 把函數返回類型放到函數名字之前,這種寫法就是前置返回類型

  • c++11中引入了一種新的寫法,後置返回類型:就是在函數聲明和定義中,把返回類型寫在參數列表之後,
    前面放auto,表示函數返回類型放到參數類表之後

#include <iostream>
#include <cstring>

using namespace std;

//auto :變量自動類型推斷
//函數聲明
auto func123(int a, int b) -> void;

auto func123(int a, int b) -> void {
    cout << "後置函數類型" << endl;
    return;
}

int main() {
    func123(1, 2);
    return 0;
}

2. 內聯函數

  • 函數定義前添加關鍵字inline,導致該函數變成了內聯函數

  • 函數體很小,調用有很頻繁,就需要引入inline(內聯)函數

  • inline影響編譯器,在編譯階段對inline這種函數進行處理,系統嘗試將調用該函數的動作替換爲函數本體,通過這種方式來提升性能

  • inline只是開發者對編譯器的一個建議,編譯器可以嘗試去做,也可以不去做,取決與編譯器的診斷功能,決定權在編譯器

  • 內聯函數定義要放在頭文件中,這樣需要源代碼用到內聯函數的.cpp文件都能夠通過include把內聯函數的包含進來,
    以便找到這個函數的本體源代碼並嘗試將該函數的調用替換爲函數體內的語句

優缺點

  1. 提升效率

  2. 代碼膨脹,所以內聯函數函數體儘量要小

  3. 注意: 不同編譯器對inline函數處理不同,inline函數儘量簡單,代碼儘可能少,減少循環,分支遞歸調用盡量不要出現在inline函數中,編譯器有可能拒絕這個函數成爲inline函數

  4. constexpr函數: constexpr int mf(),需要寫的非常簡單,可以看成是更嚴格的一種內聯函數

  5. #define宏展開也類似內聯函數,類型檢查?

3. 函數雜合用法總結

  • 函數返回類型爲void,表示函數不返回任何類型,但是我們可以調用一個返回類型是void函數讓他作爲另外一個返回類型是void的函數返回值
#include <iostream>
#include <cstring>

using namespace std;

void funca() {

}

void funcb() {
    //    可行
    // return;
     return funca();
    // 不可行
//    return void;
}

int main() {
    return 0;
}
  • 函數返回指針和返回引用的情況
#include <iostream>
#include <cstring>

using namespace std;

int *myfunc() {
    int tempvalue = 9;
    return &tempvalue;
}

int main() {
    int *p = myfunc();
    // 函數調用完成後,內存已經被釋放了, 所以這個地址是不可空的,程序會崩潰
    // 解決方案是把 int tempvalue = 9 寫成全局變量
    *p = 6;

    return 0;
}
#include <iostream>
#include <cstring>

using namespace std;

int &myfunc() {
    int tempvalue = 9;
    cout << &tempvalue << endl;
    return tempvalue;
}

int main() {
    int &k = myfunc();
    //還是不屬於可控的地址,不能這麼使用
    cout << k << endl;

    return 0;
}
  • 視頻中是windows系統,下面代碼可行,可是LZ是lunux系統,下段代碼還是會崩潰,所以這種綜合考慮來看,慎用!
#include <iostream>
#include <cstring>

using namespace std;

int &myfunc() {
    int tempvalue = 9;
    cout << &tempvalue << endl;
    return tempvalue;
}

int main() {
    int k = myfunc();
    cout << k << endl;
    return 0;
}
  • 不帶形參的函數定義:沒有形參可以表示形參列表爲空,或者(void)
#include <iostream>
#include <cstring>

using namespace std;

int myfunc(void) {

    return 1;
}

int main() {
    int k = myfunc();
    cout << k << endl;

    return 0;
}
  • 如果一個函數不調用的話,則該函數可以只有聲明部分,沒有定義部分

  • 普通函數(非內聯函數)定義只能定義一次,聲明可以聲明多次,一般函數定義.cpp文件會include自己的函數聲明.h文件

  • 引用做函數形參 void func(int &ta, int &tb),使用引用可以提升效率,函數可以返回多個返回值,在c++中更習慣用引用類型的形參,來取代指針類型的形參,提倡在c++中多使用引用類型的形參

#include <iostream>
#include <cstring>

using namespace std;

struct student {
    int num;
    //  ...
};
void func(struct student &stu) {
    stu.num = 100;
}
int main() {
    struct student stu0;
    func(stu0);
    cout << stu0.num << endl;

    return 0;
}
  • c++中,函數允許同名,但是形參列表的參數類型或者數量應該不同
#include <iostream>
#include <cstring>

using namespace std;

void func0(int i) {}

//void func(const int i){} 這種重名,不可行

void func0(float i) {}

void func0(int i, int j) {}

int main() {

    return 0;
}

4. const char*, char const*, char* const三者的區別

  • 原始代碼
#include <iostream>
#include <cstring>

using namespace std;

int main() {
    char str[] = "I am Felaim!";
    char *p;
    p = str;
    *p = 'U';
    p++;
    cout << str << endl;
    return 0;
}
  • const char *p, p指向的目標不能通過p來改變
#include <iostream>
#include <cstring>

using namespace std;

int main() {
    char str[] = "I am Felaim!";
    const char *p;
    p = str;
    // 這段相當於用p來修改p指向的內存
//    *p = 'U';
//    但是p指向的地址可以修改
    p++;
    str[0] = 'U';
    cout << str << endl;
    return 0;
}
  • char const * 等價於 const char *

  • char * const p, p指向的內存是固定的,不能改變,但是指向內存的內容可以修改

#include <iostream>
#include <cstring>

using namespace std;

int main() {
    char str[] = "I am Felaim!";
//    定義的時候必須初始化, p一旦指向一個內存之後,就不可以指向其他內存
    char *const p = str;
    // p可以修改對應指向內存的內容
    *p = 'U';
//    p指向的地址不可以修改
//    p++;
    cout << str << endl;
    return 0;
}
  • const char* const p:p指向的地址和指向地址的內存均不可以改變
#include <iostream>
#include <cstring>

using namespace std;

int main() {
    char str[] = "I am Felaim!";
//    定義的時候必須初始化, p一旦指向一個內存之後,就不可以指向其他內存
    const char *const p = str;
    // p不可以修改對應指向內存的內容
//    *p = 'U';
//    p指向的地址也不可以修改
//    p++;
    cout << str << endl;
    return 0;
}
  • 其他引用與const的用法範例
#include <iostream>
#include <cstring>

using namespace std;

int main() {
    int i = 100;
    // 代表a的內容不能修改
    const int &a = i;
//    不可行
//    a = 400;

//不可行
//    int &b = 31;
//可行方案,給b分配了內存,所以可行
    const int &b = 31;
    cout << &b << endl;
//可行
    int &c = i;
    return 0;
}

5. 函數形參中帶const

  • 優點:
  1. 防止無意中修改形參值,導致實參值被無意修改

  2. 實參類型可以更加靈活(既可以使用普通類型,也可以使用常量類型即加const的變量)

  • 範例一
#include <iostream>
#include <cstring>

using namespace std;
typedef struct student {
    int num;
    //  ...
} student;

// 學習在形參中增加const的寫法的習慣
//void func(const student &stu) {
//    // 不會無意的修改其形參值
//    //    stu.num = 10086;
//}

void func(student &stu) {
    stu.num = 10086;
}

int main() {
    student stu1;
    stu1.num = 1000;
    func(stu1);
    cout << stu1.num << endl;
    
    const student &stu2 = stu1;
//    如果使用func(student &stu)則會報錯
//    func(stu2);
    
    return 0;
}
  • 範例二
#include <iostream>
#include <cstring>

using namespace std;

void func2(int &i) {
}

void func3(const int &i) {
}

int main() {
    int aaa = 10;
//    可行
    func2(aaa);
//    不可行
//    func2(2);

//    可行, 進一步驗證實參類型可以更加靈活
    func3(2);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章