C++ 函數詳解

C++函數徹底兼容C函數定義的風格,可是也作出了一些改進。一方面,C++函數容許使用缺省參數值佔位參數;另外一方面,C++提供了重要的函數重載機制;另外,爲了解決C中臭名昭著的宏缺陷問題,C++提供了內聯函數的機制(C99以後也支持這個特性)。從C++11開始,函數定義支持後置返回類型數組

1、缺省參數值

C++中能夠在函數聲明時爲參數提供一個默認值,當函數調用時若是不提供實參,就使用這個默認值:函數

int func(int x = 0);

int main(int argc, char *argv[])
{
    printf("func() = %d\n", func());
    return 0;
}

int func(int x)
{
    return x;
}

注意:默認值只在函數聲明時有效,雖然函數定義中也能夠寫默認值,可是會被函數聲明中的默認值覆蓋。優化

函數默認參數的規則是:指針

  • 參數的默認值須要從右向左提供
  • 函數調用時若是使用了默認值,那麼後續的參數都必須使用默認值

2、函數的佔位參數

在C++中能夠爲函數提供佔位參數。佔位參數只有參數類型聲明,可是沒有參數名;這樣,在函數的實現中是沒法使用這個參數的:code

int func(int)
{
    return 0;
}

int main(int argc, char *argv[])
{
    func(1); // 雖然參數無心義,可是仍是要提供來經過編譯。
    return 0;
}

佔位參數存在的意義是:兼容C語言中可能出現的不規範的寫法,也能夠配合默認值來一塊兒使用。作用域

3、函數重載

若是同一個做用域內的幾個函數名稱相同,可是參數列表不一樣,那它們就是重載函數:編譯器

void print(const char *cp);
void print(const int *begin, const int *end);
void print(const int ia[], size_t size);

函數重載有如下幾個注意點:編譯

(3.1) 函數重載必須至少要知足的條件

  • 參數個數不一樣
  • 參數類型不一樣
  • 參數順序不一樣

注意:返回值類型不一樣不能做爲重載的條件。class

(3.2) const與重載

若是函數以值傳遞參數,那麼const不能構成重載:擴展

void func(int arg);
void func(const int arg); // 錯誤:對func的重定義

可是,若是以指針或者引用來傳遞參數,那麼const就能夠構成重載了:

void func1(int *arg);           
void func1(const int *arg);         // 正確:聲明瞭新函數
void func1(const int *const arg);   // 正確:聲明瞭新函數

void func2(int &arg); 
void func2(const int &arg);         // 正確:聲明瞭新函數

另外一方面,const成員函數非const成員函數一樣能夠構成函數重載:

class object
{
public:
    int func();
    const int func() const; // 正確:聲明瞭新函數
};

(3.3) 重載函數的肯定

編譯器會如下面的順序肯定要調用的函數:

  1. 精確匹配:
    • 實參的類型和形參徹底相同。
    • 實參從數組類型或者函數類型轉換爲對應的指針類型。
    • 向實參添加頂層const或者從實參中移除頂層const
  2. 經過const轉換實現的類型匹配。
  3. 經過類型提高實現的匹配。
  4. 經過算數類型轉換實現的匹配。
  5. 經過類類型轉換實現的匹配。

(3.4) 重載與函數指針

因爲編譯器須要根據重載規則去挑選與函數指針參數列表一致的函數,而且要嚴格地匹配函數類型與函數指針的類型,所以沒法直接經過函數名獲得重載函數的入口地址:

void func(int);
void func(double);

int main(int argc, char *argv[])
{
    void * v = func                 // 錯誤:沒法經過函數名獲得函數的地址。
    void(*pFunc1)(int) = func;      // 正確:得到了void func(int)的入口地址。
    void(*pFunc2)(double) = func;   // 正確:得到了void func(double)的入口地址。
    return 0;
}

(3.5) 讓編譯器以C語言方式編譯函數

爲了兼容舊有的C語言代碼庫,必須以C語言的編譯規則來編譯函數,所以須要使用以下的方式:

  • __cplusplus宏來檢查是否使用了C++
  • extern "C"來讓編譯器以C語言方式編譯函數
#ifdef __cplusplus
extern "C" {
#endif
    
    void func(); // 這個函數將以C語言的方式編譯。

#ifdef __cplusplus
}
#endif

4、內聯函數

C++中推薦之內聯函數來代替宏代碼片斷。C++一樣以直接替換代碼塊的方式來處理內聯函數,同時沒有著名的宏缺陷問題。可使用inline關鍵字來請求編譯器將函數之內聯函數的方式處理 (編譯器可能忽略這個請求):

inline void func() {...}

注意:

  • 內聯函數聲明時inline必須和函數定義結合在一塊兒,不然編譯器會忽略該請求。

  • 現代的編譯器會進行優化,一些函數即便沒有inline,也可能會被內聯編譯。
  • 一些編譯器提供了一些擴展語法,能夠對函數進行強制的內聯操做:
    • VC:__forceinline
    • g++:__attribute__((always_inline))

內聯函數存在一些限制:

  • 不能存在循環語句
  • 不能存在過多的條件判斷語句
  • 函數體不能太長
  • 不能取函數的地址
  • 內斂函數聲明必須在調用這個函數以前

5、後置返回類型

C++11提供了函數的後置返回類型,用於兼容自動類型推斷:

auto func() -> void {} // 等價於 void func() {}
相關文章
相關標籤/搜索