概述
創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)由有經(jīng)驗(yàn)的網(wǎng)站設(shè)計(jì)師、開發(fā)人員和項(xiàng)目經(jīng)理組成的專業(yè)建站團(tuán)隊(duì),負(fù)責(zé)網(wǎng)站視覺設(shè)計(jì)、用戶體驗(yàn)優(yōu)化、交互設(shè)計(jì)和前端開發(fā)等方面的工作,以確保網(wǎng)站外觀精美、成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)易于使用并且具有良好的響應(yīng)性。
一組執(zhí)行任務(wù)的語句都可以視為一個(gè)函數(shù),一個(gè)可調(diào)用對象。在程序設(shè)計(jì)的過程中,我們習(xí)慣于把那些具有復(fù)用性的一組語句抽象為函數(shù),把變化的部分抽象為函數(shù)的參數(shù)。
函數(shù)的使用能夠極大的極少代碼重復(fù)率,提高代碼的靈活性。
C++中具有函數(shù)這種行為的方式有很多。就函數(shù)調(diào)用方式而言
func(param1, param2);
這兒使用func作為函數(shù)調(diào)用名,param1和param2為函數(shù)參數(shù)。在C++中就func的類型,可能為:
下面就這幾種函數(shù)展開介紹
簡單函數(shù)形式
普通函數(shù)
這種函數(shù)定義比較簡單,一般聲明在一個(gè)文件開頭。如下:
#include// 普通函數(shù)聲明和定義 int func_add(int a, int b) { return a + b; } int main() { int a = 10; int b = 20; int sum = func_add(a, b); std::cout << a << "+" << b << "is : " << sum << std::endl; getchar(); return 0; }
類成員函數(shù)
在一個(gè)類class中定義的函數(shù)一般稱為類的方法,分為成員方法和靜態(tài)方法,區(qū)別是成員方法的參數(shù)列表中隱含著類this指針。
#includeclass Calcu { public: int base = 20; // 類的成員方法,參數(shù)包含this指針 int class_func_add(const int a, const int b) const { return this->base + a + b; }; // 類的靜態(tài)成員方法,不包含this指針 static int class_static_func_add(const int a, const int b) { return a + b; }; }; int main(void) { Calcu obj; int a = 10; int b = 20; // 類普通成員方法調(diào)用如下 obj.class_func_add(a, b); // 類靜態(tài)成員方法調(diào)用如下 obj.class_static_func_add(a, b); Calcu::class_static_func_add(a, b); getchar(); return 0; }
仿函數(shù)
仿函數(shù)是使用類來模擬函數(shù)調(diào)用行為,我們只要重載一個(gè)類的operator()方法,即可像調(diào)用一個(gè)函數(shù)一樣調(diào)用類。這種方式用得比較少。
class ImitateAdd { public: int operator()(const int a, const int b) const { return a + b; }; }; int main() { // 首先創(chuàng)建一個(gè)仿函數(shù)對象,然后調(diào)用()運(yùn)算符模擬函數(shù)調(diào)用 ImitateAdd imitate; imitate(5, 10); getchar(); return 0; }
函數(shù)指針
顧名思義,函數(shù)指針可以理解為指向函數(shù)的指針??梢詫⒑瘮?shù)名賦值給相同類型的函數(shù)指針,通過調(diào)用函數(shù)指針實(shí)現(xiàn)調(diào)用函數(shù)。
函數(shù)指針是標(biāo)準(zhǔn)的C/C++的回調(diào)函數(shù)的使用解決方案,本身提供了很大的靈活性。
#include// 聲明一個(gè)compute函數(shù)指針,函數(shù)參數(shù)為兩個(gè)int型,返回值為int型 int (*compute)(int, int); int max(int x, int y) { return x >= y ? x : y; } int min(int x, int y) { return x <= y ? x : y; } int add(int x, int y) { return x + y; } int multiply(int x, int y) { return x * y; } // 一個(gè)包含函數(shù)指針作為回調(diào)的函數(shù) int compute_x_y(int x, int y, int(*compute)(int, int)) { // 調(diào)用回調(diào)函數(shù) return compute(x, y); } int main(void) { int x = 2; int y = 5; std::cout << "max: " << compute_x_y(x, y, max) << std::endl; // max: 5 std::cout << "min: " << compute_x_y(x, y, min) << std::endl; // min: 2 std::cout << "add: " << compute_x_y(x, y, add) << std::endl; // add: 7 std::cout << "multiply: " << compute_x_y(x, y, multiply) << std::endl; // multiply: 10 // 無捕獲的lambda可以轉(zhuǎn)換為同類型的函數(shù)指針 auto sum = [](int x, int y)->int{ return x + y; }; std::cout << "sum: " << compute_x_y(x, y, sum) << std::endl; // sum: 7 getchar(); return 0; }
Lambda函數(shù)
Lambda函數(shù)定義
Lambda函數(shù),又可以稱為Lambda表達(dá)式或者匿名函數(shù),在C++11中加入標(biāo)準(zhǔn)。定義形式如下:
[captures] (params) -> return_type { statments;}
其中:
需要注意:
下面都是有效的匿名函數(shù)定義
auto func1 = [](int x, int y) -> int { return x + y; }; auto func2 = [](int x, int y) { return x > y; }; // 省略返回值類型 auto func3 = [] { global_ip = 0; }; // 省略參數(shù)部分
Lambda函數(shù)捕獲列表
為了能夠在Lambda函數(shù)中使用外部作用域中的變量,需要在[]中指定使用哪些變量。
下面是各種捕獲選項(xiàng):
只有l(wèi)ambda函數(shù)沒有指定任何捕獲時(shí),才可以顯式轉(zhuǎn)換成一個(gè)具有相同聲明形式函數(shù)指針
auto lambda_func_sum = [](int x, int y) { return x + y; }; // 定義lambda函數(shù) void (*func_ptr)(int, int) = lambda_func_sum; // 將lambda函數(shù)賦值給函數(shù)指針 func_ptr(10, 20); // 調(diào)用函數(shù)指針
std::function函數(shù)包裝
std::function定義
std::function在C++11后加入標(biāo)準(zhǔn),可以用它來描述C++中所有可調(diào)用實(shí)體,它是是可調(diào)用對象的包裝器,聲明如下:
#include// 聲明一個(gè)返回值為int,參數(shù)為兩個(gè)int的可調(diào)用對象類型 std::function Func;
使用之前需要導(dǎo)入
其他函數(shù)實(shí)體轉(zhuǎn)化為std::function
std::function強(qiáng)大的地方在于,它能夠兼容所有具有相同參數(shù)類型的函數(shù)實(shí)體。
相比較于函數(shù)指針,std::function能兼容帶捕獲的lambda函數(shù),而且對類成員函數(shù)提供支持。
#include#include std::function SumFunction; // 普通函數(shù) int func_sum(int a, int b) { return a + b; } class Calcu { public: int base = 20; // 類的成員方法,參數(shù)包含this指針 int class_func_sum(const int a, const int b) const { return this->base + a + b; }; // 類的靜態(tài)成員方法,不包含this指針 static int class_static_func_sum(const int a, const int b) { return a + b; }; }; // 仿函數(shù) class ImitateAdd { public: int operator()(const int a, const int b) const { return a + b; }; }; // lambda函數(shù) auto lambda_func_sum = [](int a, int b) -> int { return a + b; }; // 函數(shù)指針 int (*func_pointer)(int, int); int main(void) { int x = 2; int y = 5; // 普通函數(shù) SumFunction = func_sum; int sum = SumFunction(x, y); std::cout << "func_sum:" << sum << std::endl; // 類成員函數(shù) Calcu obj; SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, std::placeholders::_2); // 綁定this對象 sum = SumFunction(x, y); std::cout << "Calcu::class_func_sum:" << sum << std::endl; // 類靜態(tài)函數(shù) SumFunction = Calcu::class_static_func_sum; sum = SumFunction(x, y); std::cout << "Calcu::class_static_func_sum:" << sum << std::endl; // lambda函數(shù) SumFunction = lambda_func_sum; sum = SumFunction(x, y); std::cout << "lambda_func_sum:" << sum << std::endl; // 帶捕獲的lambda函數(shù) int base = 10; auto lambda_func_with_capture_sum = [&base](int x, int y)->int { return x + y + base; }; SumFunction = lambda_func_with_capture_sum; sum = SumFunction(x, y); std::cout << "lambda_func_with_capture_sum:" << sum << std::endl; // 仿函數(shù) ImitateAdd imitate; SumFunction = imitate; sum = SumFunction(x, y); std::cout << "imitate func:" << sum << std::endl; // 函數(shù)指針 func_pointer = func_sum; SumFunction = func_pointer; sum = SumFunction(x, y); std::cout << "function pointer:" << sum << std::endl; getchar(); return 0; }
最后的輸出如下:
func_sum:7
Calcu::class_func_sum:27
Calcu::class_static_func_sum:7
lambda_func_sum:7
lambda_func_with_capture_sum:17
imitate func:7
function pointer:7
其中需要注意對于類成員函數(shù),因?yàn)轭惓蓡T函數(shù)包含this指針參數(shù),所以單獨(dú)使用std::function是不夠的,還需要結(jié)合使用std::bind函數(shù)綁定this指針以及參數(shù)列表。
std::bind參數(shù)綁定規(guī)則
在使用std::bind綁定類成員函數(shù)的時(shí)候需要注意綁定參數(shù)順序:
// 承接上面的例子 SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, std::placeholders::_2); SumFunction(x, y);
看下面的例子:
// 綁定成員函數(shù)第一個(gè)參數(shù)為4,第二個(gè)參數(shù)為6 SumFunction = std::bind(&Calcu::class_func_sum, obj, 4, 6); SumFunction(); // 值為 10 // 綁定成員函數(shù)第一個(gè)參數(shù)為調(diào)用時(shí)的第一個(gè)參數(shù),第二個(gè)參數(shù)為10 SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, 10); SumFunction(5); // 值為 15 // 綁定成員函數(shù)第一個(gè)參數(shù)為調(diào)用時(shí)的第二個(gè)參數(shù),第一個(gè)參數(shù)為調(diào)用時(shí)的第二個(gè)參數(shù) SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_2, std::placeholders::_1); SumFunction(5, 10); // 值為 15
對于非類成員對象,一般直接賦值即可,會自動進(jìn)行轉(zhuǎn)換并綁定參數(shù),當(dāng)然也可以使用std::bind指定參數(shù)綁定行為;
#include#include // 按照順序輸出x, y, x void print_func(int x, int y, int z) { std::cout << x << " " << y << " " << z << std::endl; } std::function Func; int main() { Func = std::bind(&print_func, 1, 2, 3); Func(4, 5, 6); // 輸出: 1 2 3 Func = std::bind(&print_func, std::placeholders::_2, std::placeholders::_1, 3); Func(1, 2, 7); // 輸出: 2 1 3 int n = 10; Func = std::bind(&print_func, std::placeholders::_1, std::placeholders::_1, n); Func(5, 6, 7); // 輸出: 5 5 10 getchar(); return 0; }
需要注意:就算是綁定的時(shí)候指定了默認(rèn)參數(shù),在調(diào)用的時(shí)候也需要指定相同的參數(shù)個(gè)數(shù)(雖然不會起作用),否則編譯不通過。
關(guān)于回調(diào)函數(shù)
回調(diào)就是通過把函數(shù)等作為另外一個(gè)函數(shù)的參數(shù)的形式,在調(diào)用者層指定被調(diào)用者行為的方式。
通過上面的介紹,我們知道,可以使用函數(shù)指針,以及std::function作為函數(shù)參數(shù)類型,從而實(shí)現(xiàn)回調(diào)函數(shù):
#include#include std::function ComputeFunction; int (*compute_pointer)(int, int); int compute1(int x, int y, ComputeFunction func) { // do something return func(x, y); } int compute2(int x, int y, compute_pointer func) { // do something return func(x, y); } // 調(diào)用方式參考上面關(guān)于函數(shù)指針和std::function的例子
以上兩種方式,對于一般函數(shù),簡單lambda函數(shù)而言是等效的。
但是如果對于帶有捕獲的lambda函數(shù),類成員函數(shù),有特殊參數(shù)綁定需要的場景,則只能使用std::function。
其實(shí)還有很多其他的實(shí)現(xiàn)回調(diào)函數(shù)的方式,如下面的標(biāo)準(zhǔn)面向?qū)ο蟮膶?shí)現(xiàn):
#include// 定義標(biāo)準(zhǔn)的回調(diào)接口 class ComputeFunc { public: virtual int compute(int x, int y) const = 0; }; // 實(shí)現(xiàn)回調(diào)接口 class ComputeAdd : public ComputeFunc { public: int compute(int x, int y) const override { return x + y; } }; int compute3(int x, int y, const ComputeFunc& compute) { // 調(diào)用接口方法 return compute.compute(x, y); } // 調(diào)用方法如下 int main() { ComputeAdd add_func; // 創(chuàng)建一個(gè)調(diào)用實(shí)例 int sum = compute3(3, 4, add_func); // 傳入調(diào)用實(shí)例 }
面向?qū)ο蟮姆绞礁屿`活,因?yàn)檫@個(gè)回調(diào)的對象可以有很復(fù)雜的行為。
以上三種方法各有各的好處,根據(jù)你需要實(shí)現(xiàn)的功能的復(fù)雜性,擴(kuò)展性和應(yīng)用場景等決定使用。
另外,這些函數(shù)類型的參數(shù)可能為空,在調(diào)用之前,應(yīng)該檢查是否可以調(diào)用,如檢查函數(shù)指針是否為空。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對創(chuàng)新互聯(lián)的支持。