C++中函數(shù)使用的基本知識學習教程
函數(shù)是執(zhí)行某種操作的代碼塊。函數(shù)可以選擇性地定義使調用方可以將實參傳遞到函數(shù)中的輸入形參。函數(shù)可以選擇性地返回值作為輸出。函數(shù)可用于在單個可重用塊中封裝常用操作(理想情況是使用可清晰地描述函數(shù)行為的名稱)。以下函數(shù)從調用方接受兩個整數(shù)并返回其總和;a 和 b 是 int 類型的參數(shù)。
int sum(int a, int b) { return a + b; }
可以從程序中任意數(shù)量的位置調用函數(shù)。傳遞給函數(shù)的值是實參,其類型必須與函數(shù)定義中的形參類型兼容。
int main() { int i = sum(10, 32); int j = sum(i, 66); cout << "The value of j is" << j << endl; // 108 }
對于函數(shù)長度沒有實際限制,不過良好的設計應以執(zhí)行單個明確定義的任務的函數(shù)為目標。復雜算法應盡可能分解成易于理解的更簡單函數(shù)。
在類范圍中定義的函數(shù)稱為成員函數(shù)。在 C++ 中(與其他語言不同),函數(shù)還可以在命名空間范圍中定義(包括隱式全局命名空間)。這類函數(shù)稱為 free 函數(shù)或非成員函數(shù);它們在標準庫中廣泛使用。
函數(shù)聲明的各個部分
最小函數(shù)聲明包含返回類型、函數(shù)名和參數(shù)列表(可能為空),以及向編譯器提供附加說明的可選關鍵字。函數(shù)定義包含聲明以及函數(shù)體(這是大括號之間的所有代碼)。后接分號的函數(shù)聲明可以出現(xiàn)在程序中的多個位置處。它必須在每個翻譯單元中對該函數(shù)的任何調用之前出現(xiàn)。根據(jù)單個定義規(guī)則 (ODR),函數(shù)定義必須僅在程序中出現(xiàn)一次。
函數(shù)聲明的必需部分有:
返回類型,指定函數(shù)將返回的值的類型,如果不返回任何值,則為 void。在 C++11 中,auto 是有效返回類型,可指示編譯器從返回語句推斷類型。在 C++14 中,還允許使用 decltype(auto)。有關詳細信息,請參閱下面的“返回類型中的類型推導”。
函數(shù)名,必須以字母或下劃線開頭,不能包含空格。一般而言,標準庫函數(shù)名中的前導下劃線指示私有成員函數(shù),或不是供你的代碼使用的非成員函數(shù)。
參數(shù)列表(一組用大括號限定、逗號分隔的零個或多個參數(shù)),指定類型以及可以用于在函數(shù)體內訪問值的可選局部變量名。
函數(shù)聲明的可選部分有:
constexpr,指示函數(shù)的返回值是常量值,可以在編譯時進行計算。
constexpr float exp(float x, int n) { return n == 0 ? 1 : n % 2 == 0 ? exp(x * x, n / 2) : exp(x * x, (n - 1) / 2) * x; };
其 linkage 規(guī)范(extern 或 static)。
Declare printf with C linkage. extern "C" int printf( const char *fmt, ... );
inline,指示編譯器將對函數(shù)的每個調用替換為函數(shù)代碼本身。在某個函數(shù)快速執(zhí)行并且在性能關鍵代碼段中重復調用的情況下,內聯(lián)可以幫助提高性能。
inline double Account::GetBalance() { return balance; }
noexcept,指定函數(shù)是否可以引發(fā)異常。在下面的示例中,函數(shù)在 is_pod 表達式計算結果為 true 時不引發(fā)異常。
#include <type_traits> template <typename T> T copy_object(T& obj) noexcept(std::is_pod<T>) {...}
僅限成員函數(shù))cv 限定符,指定函數(shù)是 const 還是 volatile。
(僅限成員函數(shù))virtual、override 或 final。 virtual 指定可以在派生類中重寫函數(shù)。 override 表示派生類中的函數(shù)在重寫虛函數(shù)。 final 表示函數(shù)不能在任何進一步的派生類中進行重寫。
(僅限成員函數(shù)僅)應用于成員函數(shù)的 static 表示函數(shù)不與類的任何對象實例關聯(lián)。
(僅限非靜態(tài)成員函數(shù))ref 限定符,向編譯器指定隱式對象參數(shù) (*this) 是右值引用與左值引用時要選擇的函數(shù)重載。
下圖顯示了函數(shù)定義的各個部分?;疑珔^(qū)域是函數(shù)體。
函數(shù)定義部分
函數(shù)定義
函數(shù)體內聲明的變量稱為局部變量。它們會在函數(shù)退出時超出范圍;因此,函數(shù)應永遠不返回對局部變量的引用!
函數(shù)模板
函數(shù)模板類似于類模板;它基于模板參數(shù)生成具體功能。在許多情況下,模板能夠推斷類型參數(shù),因此無需顯式指定它們。
template<typename Lhs, typename Rhs> auto Add2(const Lhs& lhs, const Rhs& rhs) { return lhs + rhs; } auto a = Add2(3.13, 2.895); // a is a double auto b = Add2(string{ "Hello" }, string{ " World" }); // b is a std::string 有關詳細信息,請參閱函數(shù)模板
函數(shù)形參和實參
函數(shù)具有零種或多種類型的逗號分隔參數(shù)列表,其中每個參數(shù)都具有可以用于在函數(shù)體內訪問它的名稱。函數(shù)模板可以指定其他類型或值參數(shù)。調用方傳遞實參(其類型與形參列表兼容的具體值)。
默認情況下,參數(shù)通過值傳遞給函數(shù),這意味著函數(shù)會收到所傳遞的對象的副本。對于大型對象,創(chuàng)建副本可能成本高昂,并非始終必要。若要使實參通過引用(特別是左值引用)進行傳遞,請向形參添加引用限定符:
void DoSomething(std::string& input){...}
當函數(shù)修改通過引用傳遞的參數(shù)時,它會修改原始對象,而不是本地副本。若要防止函數(shù)修改這類實參,請將形參限定為
const&: void DoSomething(const std::string& input){...}
C++ 11:若要顯式處理通過右值引用或通過左值引用傳遞的實參,請對形參使用雙與號以指示通用引用:
void DoSomething(const std::string&& input){...}
只要關鍵字 void 是實參聲明列表中的第一個也是唯一一個成員,那么在形參聲明列表中使用單個關鍵字 void 聲明的函數(shù)就沒有實參。列表中的其他地方的 void 類型的參數(shù)產生錯誤。例如:
// OK same as GetTickCount() long GetTickCount( void );
請注意,盡管指定 void 參數(shù)是非法(此處所述的除外),但派生自類型 void 的類型(如指向 void 的指針和 void 的數(shù)組)可以出現(xiàn)在參數(shù)聲明列表中的任何位置。
默認參數(shù)
函數(shù)簽名中的最后一個或幾個形參可能會分配有默認實參,這意味著調用方可能會在調用函數(shù)時省略實參(除非要指定某個其他值)。
int DoSomething(int num, string str, Allocator& alloc = defaultAllocator) { ... } // OK both parameters are at end int DoSomethingElse(int num, string str = string{ "Working" }, Allocator& alloc = defaultAllocator) { ... } // C2548: 'DoMore': missing default parameter for parameter 2 int DoMore(int num = 5, // Not a trailing parameter! string str, Allocator& = defaultAllocator) {...}
函數(shù)返回類型
函數(shù)可能不會返回另一個函數(shù)或內置數(shù)組;但是,它可以返回指向這些類型的指針,或生成函數(shù)對象的 lambda。除了這些情況,函數(shù)可以返回處于范圍內的任何類型的值,或者它可以返回任何值(在這種情況下返回類型是 void)。
結尾返回類型
“普通”返回類型位于函數(shù)簽名左側。結尾返回類型位于簽名的最右側,前面帶有 -> 運算符。當返回值的類型取決于模板參數(shù)時,結尾返回類型在函數(shù)模板中尤其有用。
template<typename Lhs, typename Rhs> auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
當 auto 與結尾返回類型結合使用時,它對于 decltype 表達式生成的任何內容都只用作占位符,本身不執(zhí)行類型推導。
返回類型中的類型推導 (C++14)
在 C++14 中,可以使用 auto 指示編譯器從函數(shù)體推斷返回類型,而不必提供結尾返回類型。請注意,auto 始終推導為按值返回。使用 auto&& 可指示編譯器推導引用。
在此示例中,auto 會推導為 lhs 和 rhs 之和的非常量值副本。
template<typename Lhs, typename Rhs> auto Add2(const Lhs& lhs, const Rhs& rhs) { return lhs + rhs; //returns a non-const object by value }
請注意,auto 不會保留它推導的類型的常量性。對于返回值需要保留其參數(shù)的常量性或引用性的轉發(fā)函數(shù),可以使用 decltype(auto) 關鍵字,該關鍵字使用 decltype 類型推斷規(guī)則并保留所有類型信息。 decltype(auto) 可以用作左側的普通返回值,或結尾返回值。
下面的示例(基于來自 N3493 的代碼)演示的 decltype(auto) 用于采用在模板實例化之前未知的返回類型實現(xiàn)函數(shù)參數(shù)的完美轉發(fā)。
template<typename F, typename Tuple = tuple<T...>, int... I> decltype(auto) apply_(F&& f, Tuple&& args, index_sequence<I...>) { return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...); } template<typename F, typename Tuple = tuple<T...>, typename Indices = make_index_sequence<tuple_size<Tuple>::value >> decltype( auto) apply(F&& f, Tuple&& args) { return apply_(std::forward<F>(f), std::forward<Tuple>(args), Indices()); } }
函數(shù)局部變量
在函數(shù)主體內聲明的變量稱為局部變量。非靜態(tài)局部變量僅在函數(shù)體中可見,如果它們在堆棧上聲明,則會在函數(shù)退出時超出范圍。構造局部變量并通過值返回它時,編譯器通??梢詧?zhí)行返回值優(yōu)化以避免不必要的復制操作。如果通過引用返回局部變量,則編譯器會發(fā)出警告,因為調用方為使用該引用而進行的任何嘗試會在局部變量已銷毀之后進行。
局部靜態(tài)對象將在 atexit 指定的終止期間銷毀。如果某個靜態(tài)對象由于程序的控制流跳過了其聲明而未構造,則不會嘗試銷毀該對象。
靜態(tài)局部變量
在 C++ 中,局部變量可以聲明為靜態(tài)。變量僅在函數(shù)體中可見,但是對于函數(shù)的所有實例,存在變量的單個副本。
函數(shù)指針
C++ 通過與 C 語言相同的方式支持函數(shù)指針。但是更加類型安全的替代方法通常是使用函數(shù)對象。
建議使用 typedef 聲明函數(shù)指針類型的別名(如果聲明返回函數(shù)指針類型的函數(shù))。例如
typedef int (*fp)(int); fp myFunction(char* s); // function returning function pointer
如果不執(zhí)行此操作,則函數(shù)聲明的正確語法可以通過用函數(shù)名稱和參數(shù)列表替換標識符(上例中為 fp)來從函數(shù)指針的聲明符語法推導出,如下所示:
int (*myFunction(char* s))(int);
前面的聲明與使用上面的 typedef 的聲明等效。
相關文章
詳解c語言中的 strcpy和strncpy字符串函數(shù)使用
strcpy 和strcnpy函數(shù)是字符串復制函數(shù)。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數(shù)使用,感興趣的朋友跟隨小編要求看看吧2018-10-10