一、inline內(nèi)聯(lián)函數(shù)#
特征
- 相當(dāng)于把內(nèi)聯(lián)函數(shù)里面的內(nèi)容寫在調(diào)用內(nèi)聯(lián)函數(shù)處;
- 相當(dāng)于不用執(zhí)行進(jìn)入函數(shù)的步驟,直接執(zhí)行函數(shù)體;
- 相當(dāng)于宏,卻比宏多了類型檢查,真正具有函數(shù)特性;
- 編譯器一般不內(nèi)聯(lián)包含循環(huán)、遞歸、switch 等復(fù)雜操作的內(nèi)聯(lián)函數(shù);
- 在類聲明中定義的函數(shù),除了虛函數(shù)的其他函數(shù)都會(huì)自動(dòng)隱式地當(dāng)成內(nèi)聯(lián)函數(shù);
- 內(nèi)聯(lián)關(guān)鍵字是在編譯時(shí)建議編譯器內(nèi)聯(lián),是不是內(nèi)聯(lián)函數(shù)取決于編譯器,一個(gè)好的編譯器將會(huì)根據(jù)函數(shù)的定義體,自動(dòng)地取消不值得的內(nèi)聯(lián)(是否內(nèi)聯(lián):1、可以通過多次調(diào)用函數(shù),查看執(zhí)行文件大小,如果變大了,就證明是內(nèi)聯(lián)函數(shù);2、通過反匯編查看數(shù)據(jù))。
1.1 使用#
- inline是一種“用于實(shí)現(xiàn)的關(guān)鍵字”,而不是一種“用于聲明的關(guān)鍵字”,也就是說,如果只在生命中使用inline是沒有用的,若要成為inline函數(shù)必須在定義函數(shù)的時(shí)候添加該關(guān)鍵字。在聲明中加不加inline關(guān)鍵字都沒關(guān)系,但是為了閱讀方便,還是建議聲明和定義都加上;
- C++在類中定義函數(shù)的時(shí)候,當(dāng)函數(shù)不包含循環(huán)、遞歸、switch 等復(fù)雜操作時(shí),編譯器會(huì)進(jìn)行隱式內(nèi)聯(lián)。
- C++在類外定義函數(shù),因?yàn)榕c非inline函數(shù)不同:inline函數(shù)對(duì)編譯器而言必須是可見的,以便它能夠在調(diào)用點(diǎn)展開該函數(shù),inline函數(shù)必須在調(diào)用該函數(shù)的每個(gè)文本文件中定義。所以內(nèi)聯(lián)函數(shù)的聲明和定義建議都放在同一個(gè)頭文件,這樣另一個(gè).cpp文件#include該頭文件的時(shí)候,就把該內(nèi)聯(lián)函數(shù)的定義也包含進(jìn)來了,這就可以正常使用內(nèi)聯(lián)函數(shù)了。
聲明
1
2
|
// 聲明1(加 inline,建議使用) inline int functionName( int first, int second,...); |
定義
1
2
|
// 定義 inline int functionName( int first, int second,...) { /****/ }; |
類內(nèi)定義
1
2
3
4
|
// 類內(nèi)定義,隱式內(nèi)聯(lián) class A { int doA() { return 0; } // 隱式內(nèi)聯(lián) } |
類外定義
1
2
3
4
5
|
// 類外定義,需要顯式內(nèi)聯(lián) class A { int doA(); } inline int A::doA() { return 0; } // 需要顯式內(nèi)聯(lián) |
1.2 編譯器對(duì) inline 函數(shù)處理步驟#
- 將 inline 函數(shù)體復(fù)制到 inline 函數(shù)調(diào)用點(diǎn)處;
- 為所用 inline 函數(shù)中的局部變量分配內(nèi)存空間;
- 將 inline 函數(shù)的的輸入?yún)?shù)和返回值映射到調(diào)用方法的局部變量空間中;
- 如果 inline 函數(shù)有多個(gè)返回點(diǎn),將其轉(zhuǎn)變?yōu)?inline 函數(shù)代碼塊末尾的分支(使用 GOTO)。
1.3 優(yōu)缺點(diǎn)#
1.3.1 優(yōu)點(diǎn)#
- 內(nèi)聯(lián)函數(shù)同宏函數(shù)一樣將在被調(diào)用處進(jìn)行代碼展開,省去了參數(shù)壓棧、棧幀開辟與回收,結(jié)果返回等,從而提高程序運(yùn)行速度。
- 內(nèi)聯(lián)函數(shù)相比宏函數(shù)來說,在代碼展開時(shí),會(huì)做安全檢查或自動(dòng)類型轉(zhuǎn)換(同普通函數(shù)),而宏定義則不會(huì)。
- 在類中聲明同時(shí)定義的成員函數(shù),自動(dòng)轉(zhuǎn)化為內(nèi)聯(lián)函數(shù),因此內(nèi)聯(lián)函數(shù)可以訪問類的成員變量,宏定義則不能。
- 內(nèi)聯(lián)函數(shù)在運(yùn)行時(shí)可調(diào)試,而宏定義不可以。
1.3.2 慎用內(nèi)聯(lián)#
- 內(nèi)聯(lián)是以代碼膨脹為代價(jià),僅僅是省去了函數(shù)調(diào)用的開銷,從而提高了函數(shù)的執(zhí)行效率。如果執(zhí)行函數(shù)體內(nèi)代碼的時(shí)間,相比于函數(shù)調(diào)用的開銷較大,那么效率的收獲會(huì)很小。另一個(gè)方面,每一處內(nèi)聯(lián)函數(shù)調(diào)用都要復(fù)制代碼,將使程序總代碼量增大,消耗更多的內(nèi)存空間。
- 類的構(gòu)造函數(shù)和析構(gòu)函數(shù)容易讓人誤解成使用內(nèi)聯(lián)函數(shù)更有效。要當(dāng)心構(gòu)造函數(shù)和析構(gòu)函數(shù)可能會(huì)隱藏一些行為,如”偷偷地“執(zhí)行基類或成員對(duì)象的構(gòu)造函數(shù)和析構(gòu)函數(shù)。所以不要隨便地將構(gòu)造函數(shù)和析構(gòu)函數(shù)的定義體放在類的定義中。
1.3.3 不宜使用內(nèi)聯(lián)#
- 如果函數(shù)體內(nèi)的代碼比較長(zhǎng),使用內(nèi)聯(lián)將導(dǎo)致內(nèi)存消耗代價(jià)比較高;
- 如果函數(shù)體內(nèi)出現(xiàn)循環(huán),那么執(zhí)行函數(shù)體內(nèi)代碼的時(shí)間要比函數(shù)調(diào)用的開銷大;
1.4 虛函數(shù)(virtual)可以是內(nèi)聯(lián)函數(shù)(inline)嗎?#
- 虛函數(shù)可以是內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)是可以修飾虛函數(shù)的,但是當(dāng)虛函數(shù)表現(xiàn)多態(tài)性的時(shí)候不能內(nèi)聯(lián)。
- 內(nèi)聯(lián)是在編譯器建議編譯器內(nèi)聯(lián),而虛函數(shù)的多態(tài)性在運(yùn)行期,編譯器無法知道運(yùn)行期調(diào)用哪個(gè)代碼,因此虛函數(shù)表現(xiàn)為多態(tài)性時(shí)(運(yùn)行期)不可以內(nèi)聯(lián)。
- inline virtual 唯一可以內(nèi)聯(lián)的時(shí)候是:編譯器知道所調(diào)用的對(duì)象是哪個(gè)類(如 Base::who()),這只有在編譯器具有實(shí)際對(duì)象而不是對(duì)象的指針或引用時(shí)才會(huì)發(fā)生。
如下例程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
#include <iostream> using namespace std; class Base { public : inline virtual void who() { cout << "I am Base\n" ; } virtual ~Base() {} }; class Derived : public Base { public : inline void who() // 不寫inline時(shí)隱式內(nèi)聯(lián) { cout << "I am Derived\n" ; } }; int main() { // 此處的虛函數(shù) who(),是通過類(Base)的具體對(duì)象(b)來調(diào)用的,編譯期間就能確定了,所以它可以是內(nèi)聯(lián)的,但最終是否內(nèi)聯(lián)取決于編譯器。 Base b; b.who(); // 此處的虛函數(shù)是通過指針調(diào)用的,呈現(xiàn)多態(tài)性,需要在運(yùn)行時(shí)期間才能確定,所以不能為內(nèi)聯(lián)。 Base *ptr = new Derived(); ptr->who(); // 因?yàn)锽ase有虛析構(gòu)函數(shù)(virtual ~Base() {}),所以 delete 時(shí),會(huì)先調(diào)用派生類(Derived)析構(gòu)函數(shù),再調(diào)用基類(Base)析構(gòu)函數(shù),防止內(nèi)存泄漏。 delete ptr; ptr = nullptr; system ( "pause" ); return 0; } |
二、回調(diào)函數(shù)和普通函數(shù)#
更詳細(xì)的回調(diào)函數(shù)理解可以查看本地的這個(gè)文章【【知識(shí)點(diǎn)】10張圖讓你徹底理解回調(diào)函數(shù)】
2.1 什么是回調(diào)函數(shù)?#
把a(bǔ)函數(shù)指針像參數(shù)傳遞那樣傳給b函數(shù),而這個(gè)a函數(shù)會(huì)在某個(gè)時(shí)刻被b函數(shù)調(diào)用執(zhí)行,這就叫做回調(diào),a函數(shù)稱為回調(diào)函數(shù)。如果回調(diào)函數(shù)立即被執(zhí)行就稱為同步回調(diào),如果在之后晚點(diǎn)的某個(gè)時(shí)間再執(zhí)行,則稱之為異步回調(diào)。
2.2 為什么要使用回調(diào)函數(shù)?#
先拋出答案:回調(diào)函數(shù)的好處和作用,那就是解耦,對(duì),就是這么簡(jiǎn)單的答案,就是因?yàn)檫@個(gè)特點(diǎn),普通函數(shù)代替不了回調(diào)函數(shù)。
如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
int Callback_1() { printf ( "Hello" ); printf ( "This is Callback_1 " ); return 0; } int Callback_2() { printf ( "Hello" ); printf ( "This is Callback_2 " ); return 0; } |
發(fā)現(xiàn)以上代碼是可以解耦的,因?yàn)閮蓚€(gè)函數(shù)都執(zhí)行了printf("Hello"),這個(gè)時(shí)候我們可以通過回調(diào)的方式進(jìn)行解耦,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include<stdio.h> int Callback_1() // Callback Function 1 { printf ( "This is Callback_1 " ); return 0; } int Callback_2() // Callback Function 2 { printf ( "This is Callback_2 " ); return 0; } int Handle( int (*Callback)()) { printf ( "Entering Handle Function. " ); Callback(); printf ( "Leaving Handle Function. " ); } int main() { printf ( "Entering Main Function. " ); Handle(Callback_1); Handle(Callback_2); printf ( "Leaving Main Function. " ); return 0; } |
像這樣我們就減少了重復(fù)代碼啦,也就是解耦。這是使用普通函數(shù)調(diào)用無法做到的。
回調(diào)函數(shù)和普通函數(shù)有什么區(qū)別?
1、對(duì)普通函數(shù)的調(diào)用:調(diào)用程序發(fā)出對(duì)普通函數(shù)的調(diào)用后,程序執(zhí)行立即轉(zhuǎn)向被調(diào)用函數(shù)執(zhí)行,直到被調(diào)用函數(shù)執(zhí)行完畢后,再返回調(diào)用程序繼續(xù)執(zhí)行。從發(fā)出調(diào)用的程序的角度看,這個(gè)過程為“調(diào)用-->等待被調(diào)用函數(shù)執(zhí)行完畢-->繼續(xù)執(zhí)行”。
2、對(duì)回調(diào)函數(shù)調(diào)用:調(diào)用程序發(fā)出對(duì)回調(diào)函數(shù)的調(diào)用后,不等函數(shù)執(zhí)行完畢,立即返回并繼續(xù)執(zhí)行。這樣,調(diào)用程序執(zhí)和被調(diào)用函數(shù)同時(shí)在執(zhí)行。當(dāng)被調(diào)函數(shù)執(zhí)行完畢后,被調(diào)函數(shù)會(huì)反過來調(diào)用某個(gè)事先指定函數(shù),以通知調(diào)用程序:函數(shù)調(diào)用結(jié)束。這個(gè)過程稱為回調(diào)(Callback),這正是回調(diào)函數(shù)名稱的由來。
總結(jié)
到此這篇關(guān)于C++知識(shí)點(diǎn)之inline函數(shù)、回調(diào)函數(shù)和普通函數(shù)的文章就介紹到這了,更多相關(guān)inline函數(shù)、回調(diào)函數(shù)和普通函數(shù)內(nèi)容請(qǐng)搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://www.cnblogs.com/lcgbk/p/15000506.html