函數調用的效率

一、函數、宏、內聯函數
1、函數
    調用函數的開銷大致可分兩個部分:傳遞參數的開銷和保存當前程序上下文信息所花費的開銷。對於
傳遞參數的開銷而言,傳遞的參數越多開銷就越大;對於保存當前程序上下文所花費的開銷而言,函數越
複雜需要花費的開銷就越大。
2、宏
    宏在某種程度上可以代替函數,避免函數調用帶來的開銷。定義完宏之後,在編譯程序時,用替代字
符串代替程序中的宏。
    對於宏而言,雖然避免了函數調用帶來的開銷,但是增加了內存的開銷。因爲在每個應用宏的地方宏
都會展開。
    另外,宏易讀性比較差,容易出現問題。例如:
#define MAX(a,b) ((a) < (b) ? (b):(a))
void main()
{
        int a = 10;
        int b = 11;
        int c = 0;
        c = MAX(a++,b++);
        cout << c << endl;
        cin >> c;
}
    我們會發現c的結果爲12,因爲宏展開之後變爲:((a++) < (b++) ? (b++) : (a++))並不是我們預想
中的結果。故一般來講最好不使用宏。
3、內聯函數
    內聯函數是避免函數調用開銷的另一種方法,與宏類似,在程序編譯是用內聯函數替換函數調用。但
只能運用與C++中,C中無法使用。與宏相比擁有參數類型檢查的優點。
    在C++類中,對於設置和讀取私有變量的函數設置爲內斂函數,有助於提高函數的效率。
內斂函數的實現有兩種方式,第一種是在函數定義時使用關鍵字inline,第二種方式時在函數定義時與函
數體一起定義。
4、遞歸與迭代
    遞歸可以使程序顯得短小精悍,然而由於函數的多層調用,會嚴重影響程序的運行效率和內存使用效
率。幸運的是在某種情況下,我們可以使用迭代來代替遞歸,避免函數調用開銷。
例子:
遞歸版計算n的階乘
long Factorial(int n)
{
        if(n == 0)
        {
                return 1;
        }
        else
        {
                return Factorial(n-1);
        }
}
迭代版計算n的階乘
long Factorial(int n)
{
        int fac = 1;
        while(n != 0)
        {
                fac *= n;
                n--;
        }
        return fac;
}
二、函數的參數傳遞
    函數參數的多少直接影響到函數調用的開銷,故編寫函數時需要選擇恰當的參數傳遞方式。我們可以
用三種方式來傳遞函數:按值傳遞、按引用傳遞和通過全局變量傳遞。
    按值傳遞參數時,需要把所需傳遞的參數全部複製到堆棧中,當函數訪問該參數時相當於訪問局部變
量,爲直接尋址;按引用傳遞參數時,複製到堆棧中的時指針所包含的地址,當函數訪問該參數時相當於
間接尋址,增加了訪問參數的時間。通過全局變量傳遞數據,避免了函數調用的開銷,擁有較高的效率,
但會影響程序的可維護性,故非在非常明確的情況下,慎用全局變量。
    當需要通過函數修改參數值時,通過引用傳遞參數或通過全局變量是不二的選擇。
    當無需通過函數修改參數值時,對於較小的參數(佔用字節少)採用按值傳遞較之按引用傳遞較好,
對於佔用較多字節的參數(結構體、類等)時,採用按引用傳遞的方式有助於減少函數參數傳遞的開銷。
三、類對象的高效初始化
1、類對象初始化
    構造一個對象類並設置其私有數據成員,通常有兩種方法,第一種是首先通過無參構造函數創建類對
象,再通過私有數據設置函數設置成員數據的值;第二種是通過帶有參數的構造函數,同時創建類對象並
設置私有數據成員的值。較第一種方法而言,第二種方式顯然擁有更高的效率,因爲,調用私有數據成員
設置函數同樣需要花費函數調用的開銷。故當我們能夠一次性完成創建與初始化時,最好採用第二種方案

低效類對象初始化:
People peo;
peo.SetName("John");
peo.SetAge(23);
peo.SetSex("man");
高效類對象初始化:
People peo("John", 23, "man");
2、基類初始化
    當創建類對象時,若該類存在基類,則先調用基類的構造函數,在不採用基類初始化方式的情況下,
系統調用基類的默認構造函數(無參構造函數)。此時若需設置基類的私有數據成員,必須調用基類的私
有成員設置函數,從而增加函數調用的開銷。故在能夠同時提供基類私有數據成員的情況下,儘量採用基
類初始化的方式初始化基類。
低效基類初始化:
Student::Student(string myName, int myAge, int mySex, int myGrade)
{
        SetName(myName);
        SetAge(myAge);
        SetSex(mySex);
        grade = myGrade;
}
高效基類初始化:
Student::Student(string myName, int myAge, int mySex, int myGrade):
People(myName, myAge, mySex, myGrade)
{
        this->grade = myGrade;
}

發佈了5 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章