C++中函數(shù)使用的基本知識學(xué)習(xí)教程
函數(shù)是執(zhí)行某種操作的代碼塊。函數(shù)可以選擇性地定義使調(diào)用方可以將實(shí)參傳遞到函數(shù)中的輸入形參。函數(shù)可以選擇性地返回值作為輸出。函數(shù)可用于在單個(gè)可重用塊中封裝常用操作(理想情況是使用可清晰地描述函數(shù)行為的名稱)。以下函數(shù)從調(diào)用方接受兩個(gè)整數(shù)并返回其總和;a 和 b 是 int 類型的參數(shù)。
int sum(int a, int b) { return a + b; }
可以從程序中任意數(shù)量的位置調(diào)用函數(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ù)長度沒有實(shí)際限制,不過良好的設(shè)計(jì)應(yīng)以執(zhí)行單個(gè)明確定義的任務(wù)的函數(shù)為目標(biāo)。復(fù)雜算法應(yīng)盡可能分解成易于理解的更簡單函數(shù)。
在類范圍中定義的函數(shù)稱為成員函數(shù)。在 C++ 中(與其他語言不同),函數(shù)還可以在命名空間范圍中定義(包括隱式全局命名空間)。這類函數(shù)稱為 free 函數(shù)或非成員函數(shù);它們在標(biāo)準(zhǔn)庫中廣泛使用。
函數(shù)聲明的各個(gè)部分
最小函數(shù)聲明包含返回類型、函數(shù)名和參數(shù)列表(可能為空),以及向編譯器提供附加說明的可選關(guān)鍵字。函數(shù)定義包含聲明以及函數(shù)體(這是大括號之間的所有代碼)。后接分號的函數(shù)聲明可以出現(xiàn)在程序中的多個(gè)位置處。它必須在每個(gè)翻譯單元中對該函數(shù)的任何調(diào)用之前出現(xiàn)。根據(jù)單個(gè)定義規(guī)則 (ODR),函數(shù)定義必須僅在程序中出現(xiàn)一次。
函數(shù)聲明的必需部分有:
返回類型,指定函數(shù)將返回的值的類型,如果不返回任何值,則為 void。在 C++11 中,auto 是有效返回類型,可指示編譯器從返回語句推斷類型。在 C++14 中,還允許使用 decltype(auto)。有關(guān)詳細(xì)信息,請參閱下面的“返回類型中的類型推導(dǎo)”。
函數(shù)名,必須以字母或下劃線開頭,不能包含空格。一般而言,標(biāo)準(zhǔn)庫函數(shù)名中的前導(dǎo)下劃線指示私有成員函數(shù),或不是供你的代碼使用的非成員函數(shù)。
參數(shù)列表(一組用大括號限定、逗號分隔的零個(gè)或多個(gè)參數(shù)),指定類型以及可以用于在函數(shù)體內(nèi)訪問值的可選局部變量名。
函數(shù)聲明的可選部分有:
constexpr,指示函數(shù)的返回值是常量值,可以在編譯時(shí)進(jìn)行計(jì)算。
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ù)的每個(gè)調(diào)用替換為函數(shù)代碼本身。在某個(gè)函數(shù)快速執(zhí)行并且在性能關(guān)鍵代碼段中重復(fù)調(diào)用的情況下,內(nèi)聯(lián)可以幫助提高性能。
inline double Account::GetBalance() { return balance; }
noexcept,指定函數(shù)是否可以引發(fā)異常。在下面的示例中,函數(shù)在 is_pod 表達(dá)式計(jì)算結(jié)果為 true 時(shí)不引發(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ù)不能在任何進(jìn)一步的派生類中進(jìn)行重寫。
(僅限成員函數(shù)僅)應(yīng)用于成員函數(shù)的 static 表示函數(shù)不與類的任何對象實(shí)例關(guān)聯(lián)。
(僅限非靜態(tài)成員函數(shù))ref 限定符,向編譯器指定隱式對象參數(shù) (*this) 是右值引用與左值引用時(shí)要選擇的函數(shù)重載。
下圖顯示了函數(shù)定義的各個(gè)部分?;疑珔^(qū)域是函數(shù)體。
函數(shù)定義部分
函數(shù)定義
函數(shù)體內(nèi)聲明的變量稱為局部變量。它們會(huì)在函數(shù)退出時(shí)超出范圍;因此,函數(shù)應(yīng)永遠(yuǎn)不返回對局部變量的引用!
函數(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 有關(guān)詳細(xì)信息,請參閱函數(shù)模板
函數(shù)形參和實(shí)參
函數(shù)具有零種或多種類型的逗號分隔參數(shù)列表,其中每個(gè)參數(shù)都具有可以用于在函數(shù)體內(nèi)訪問它的名稱。函數(shù)模板可以指定其他類型或值參數(shù)。調(diào)用方傳遞實(shí)參(其類型與形參列表兼容的具體值)。
默認(rèn)情況下,參數(shù)通過值傳遞給函數(shù),這意味著函數(shù)會(huì)收到所傳遞的對象的副本。對于大型對象,創(chuàng)建副本可能成本高昂,并非始終必要。若要使實(shí)參通過引用(特別是左值引用)進(jìn)行傳遞,請向形參添加引用限定符:
void DoSomething(std::string& input){...}
當(dāng)函數(shù)修改通過引用傳遞的參數(shù)時(shí),它會(huì)修改原始對象,而不是本地副本。若要防止函數(shù)修改這類實(shí)參,請將形參限定為
const&: void DoSomething(const std::string& input){...}
C++ 11:若要顯式處理通過右值引用或通過左值引用傳遞的實(shí)參,請對形參使用雙與號以指示通用引用:
void DoSomething(const std::string&& input){...}
只要關(guān)鍵字 void 是實(shí)參聲明列表中的第一個(gè)也是唯一一個(gè)成員,那么在形參聲明列表中使用單個(gè)關(guān)鍵字 void 聲明的函數(shù)就沒有實(shí)參。列表中的其他地方的 void 類型的參數(shù)產(chǎn)生錯(cuò)誤。例如:
// OK same as GetTickCount() long GetTickCount( void );
請注意,盡管指定 void 參數(shù)是非法(此處所述的除外),但派生自類型 void 的類型(如指向 void 的指針和 void 的數(shù)組)可以出現(xiàn)在參數(shù)聲明列表中的任何位置。
默認(rèn)參數(shù)
函數(shù)簽名中的最后一個(gè)或幾個(gè)形參可能會(huì)分配有默認(rèn)實(shí)參,這意味著調(diào)用方可能會(huì)在調(diào)用函數(shù)時(shí)省略實(shí)參(除非要指定某個(gè)其他值)。
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ù)可能不會(huì)返回另一個(gè)函數(shù)或內(nèi)置數(shù)組;但是,它可以返回指向這些類型的指針,或生成函數(shù)對象的 lambda。除了這些情況,函數(shù)可以返回處于范圍內(nèi)的任何類型的值,或者它可以返回任何值(在這種情況下返回類型是 void)。
結(jié)尾返回類型
“普通”返回類型位于函數(shù)簽名左側(cè)。結(jié)尾返回類型位于簽名的最右側(cè),前面帶有 -> 運(yùn)算符。當(dāng)返回值的類型取決于模板參數(shù)時(shí),結(jié)尾返回類型在函數(shù)模板中尤其有用。
template<typename Lhs, typename Rhs> auto Add(const Lhs& lhs, const Rhs& rhs) -> decltype(lhs + rhs) { return lhs + rhs; }
當(dāng) auto 與結(jié)尾返回類型結(jié)合使用時(shí),它對于 decltype 表達(dá)式生成的任何內(nèi)容都只用作占位符,本身不執(zhí)行類型推導(dǎo)。
返回類型中的類型推導(dǎo) (C++14)
在 C++14 中,可以使用 auto 指示編譯器從函數(shù)體推斷返回類型,而不必提供結(jié)尾返回類型。請注意,auto 始終推導(dǎo)為按值返回。使用 auto&& 可指示編譯器推導(dǎo)引用。
在此示例中,auto 會(huì)推導(dǎo)為 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 不會(huì)保留它推導(dǎo)的類型的常量性。對于返回值需要保留其參數(shù)的常量性或引用性的轉(zhuǎn)發(fā)函數(shù),可以使用 decltype(auto) 關(guān)鍵字,該關(guān)鍵字使用 decltype 類型推斷規(guī)則并保留所有類型信息。 decltype(auto) 可以用作左側(cè)的普通返回值,或結(jié)尾返回值。
下面的示例(基于來自 N3493 的代碼)演示的 decltype(auto) 用于采用在模板實(shí)例化之前未知的返回類型實(shí)現(xiàn)函數(shù)參數(shù)的完美轉(zhuǎn)發(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ù)主體內(nèi)聲明的變量稱為局部變量。非靜態(tài)局部變量僅在函數(shù)體中可見,如果它們在堆棧上聲明,則會(huì)在函數(shù)退出時(shí)超出范圍。構(gòu)造局部變量并通過值返回它時(shí),編譯器通??梢詧?zhí)行返回值優(yōu)化以避免不必要的復(fù)制操作。如果通過引用返回局部變量,則編譯器會(huì)發(fā)出警告,因?yàn)檎{(diào)用方為使用該引用而進(jìn)行的任何嘗試會(huì)在局部變量已銷毀之后進(jìn)行。
局部靜態(tài)對象將在 atexit 指定的終止期間銷毀。如果某個(gè)靜態(tài)對象由于程序的控制流跳過了其聲明而未構(gòu)造,則不會(huì)嘗試銷毀該對象。
靜態(tài)局部變量
在 C++ 中,局部變量可以聲明為靜態(tài)。變量僅在函數(shù)體中可見,但是對于函數(shù)的所有實(shí)例,存在變量的單個(gè)副本。
函數(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ù)列表替換標(biāo)識符(上例中為 fp)來從函數(shù)指針的聲明符語法推導(dǎo)出,如下所示:
int (*myFunction(char* s))(int);
前面的聲明與使用上面的 typedef 的聲明等效。
- C++編程中隊(duì)內(nèi)聯(lián)函數(shù)的理解和使用
- 深度探究C++中的函數(shù)重載的用法
- 實(shí)例講解在C++的函數(shù)中變量參數(shù)及默認(rèn)參數(shù)的使用
- C++中replace()函數(shù)使用方法匯總
- VC++ 使用 _access函數(shù)判斷文件或文件夾是否存在
- C++中strcpy函數(shù)的實(shí)現(xiàn)
- 解析C++中多層派生時(shí)的構(gòu)造函數(shù)及一些特殊形式
- 深入解析C++中派生類的構(gòu)造函數(shù)
- 詳解在C++中顯式默認(rèn)設(shè)置的函數(shù)和已刪除的函數(shù)的方法
相關(guān)文章
詳解c語言中的 strcpy和strncpy字符串函數(shù)使用
strcpy 和strcnpy函數(shù)是字符串復(fù)制函數(shù)。接下來通過本文給大家介紹c語言中的strcpy和strncpy字符串函數(shù)使用,感興趣的朋友跟隨小編要求看看吧2018-10-10C語言?超詳細(xì)講解算法的時(shí)間復(fù)雜度和空間復(fù)雜度
算法復(fù)雜度分為時(shí)間復(fù)雜度和空間復(fù)雜度。其作用:?時(shí)間復(fù)雜度是度量算法執(zhí)行的時(shí)間長短;而空間復(fù)雜度是度量算法所需存儲空間的大小2022-03-03詳解Matlab繪制3D玫瑰花的方法(內(nèi)附旋轉(zhuǎn)版本)
這篇文章主要為大家介紹了如何利用Matlab繪制3D版的玫瑰花以及旋轉(zhuǎn)版的3D玫瑰花,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以動(dòng)手試一試2022-03-03C++貪心算法實(shí)現(xiàn)活動(dòng)安排問題(實(shí)例代碼)
貪心算法(又稱貪婪算法)是指,在對問題求解時(shí),總是做出在當(dāng)前看來是最好的選擇。這篇文章主要介紹了C++貪心算法實(shí)現(xiàn)活動(dòng)安排問題,需要的朋友可以參考下2019-11-11c++ 動(dòng)態(tài)內(nèi)存分配相關(guān)總結(jié)
這篇文章主要介紹了c++ 動(dòng)態(tài)內(nèi)存分配相關(guān)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)和使用c++,感興趣的朋友可以了解下2021-02-02