国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++中各種可調用對象深入講解

2020-01-26 13:32:21
字體:
來源:轉載
供稿:網友

概述

一組執行任務的語句都可以視為一個函數,一個可調用對象。在程序設計的過程中,我們習慣于把那些具有復用性的一組語句抽象為函數,把變化的部分抽象為函數的參數。

函數的使用能夠極大的極少代碼重復率,提高代碼的靈活性。

C++中具有函數這種行為的方式有很多。就函數調用方式而言

func(param1, param2);

這兒使用func作為函數調用名,param1和param2為函數參數。在C++中就func的類型,可能為:

  • 普通函數
  • 類成員函數
  • 類靜態函數
  • 仿函數
  • 函數指針
  • lambda表達式 C++11加入標準
  • std::function C++11加入標準

下面就這幾種函數展開介紹

簡單函數形式

普通函數

這種函數定義比較簡單,一般聲明在一個文件開頭。如下:

#include <iostream>// 普通函數聲明和定義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;}

類成員函數

在一個類class中定義的函數一般稱為類的方法,分為成員方法和靜態方法,區別是成員方法的參數列表中隱含著類this指針。

#include <iostream>class Calcu{public:  int base = 20;  // 類的成員方法,參數包含this指針  int class_func_add(const int a, const int b) const { return this->base + a + b; };  // 類的靜態成員方法,不包含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;  // 類普通成員方法調用如下  obj.class_func_add(a, b);  // 類靜態成員方法調用如下  obj.class_static_func_add(a, b);  Calcu::class_static_func_add(a, b);  getchar();  return 0;}

仿函數

仿函數是使用類來模擬函數調用行為,我們只要重載一個類的operator()方法,即可像調用一個函數一樣調用類。這種方式用得比較少。

class ImitateAdd{public:  int operator()(const int a, const int b) const { return a + b; };};int main(){  // 首先創建一個仿函數對象,然后調用()運算符模擬函數調用  ImitateAdd imitate;  imitate(5, 10);  getchar();  return 0;}

函數指針

顧名思義,函數指針可以理解為指向函數的指針。可以將函數名賦值給相同類型的函數指針,通過調用函數指針實現調用函數。

函數指針是標準的C/C++的回調函數的使用解決方案,本身提供了很大的靈活性。

#include <iostream>// 聲明一個compute函數指針,函數參數為兩個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; }// 一個包含函數指針作為回調的函數int compute_x_y(int x, int y, int(*compute)(int, int)){  // 調用回調函數  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可以轉換為同類型的函數指針  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函數

Lambda函數定義

Lambda函數,又可以稱為Lambda表達式或者匿名函數,在C++11中加入標準。定義形式如下:

[captures] (params) -> return_type { statments;}

其中:

  • [captures]為捕獲列表,用于捕獲外層變量
  • (params)為匿名函數參數列表
  • ->return_type指定匿名函數返回值類型
  • { statments; }部分為函數體,包括一系列語句

需要注意:

  • 當匿名函數沒有參數時,可以省略(params)部分
  • 當匿名函數體的返回值只有一個類型或者返回值為void時,可以省略->return_type部分
  • 定義匿名函數時,一般使用auto作為匿名函數類型

下面都是有效的匿名函數定義

auto func1 = [](int x, int y) -> int { return x + y; }; auto func2 = [](int x, int y) { return x > y; }; // 省略返回值類型auto func3 = [] { global_ip = 0; }; // 省略參數部分

Lambda函數捕獲列表

為了能夠在Lambda函數中使用外部作用域中的變量,需要在[]中指定使用哪些變量。

下面是各種捕獲選項:

  • [] 不捕獲任何變量
  • [&] 捕獲外部作用域中所有變量,并作為引用在匿名函數體中使用
  • [=] 捕獲外部作用域中所有變量,并拷貝一份在匿名函數體中使用
  • [x, &y] x按值捕獲, y按引用捕獲
  • [&, x] x按值捕獲. 其它變量按引用捕獲
  • [=, &y] y按引用捕獲. 其它變量按值捕獲
  • [this] 捕獲當前類中的this指針,如果已經使用了&或者=就默認添加此選項

只有lambda函數沒有指定任何捕獲時,才可以顯式轉換成一個具有相同聲明形式函數指針

auto lambda_func_sum = [](int x, int y) { return x + y; }; // 定義lambda函數void (*func_ptr)(int, int) = lambda_func_sum; // 將lambda函數賦值給函數指針func_ptr(10, 20); // 調用函數指針

std::function函數包裝

std::function定義

std::function在C++11后加入標準,可以用它來描述C++中所有可調用實體,它是是可調用對象的包裝器,聲明如下:

#include <functional>// 聲明一個返回值為int,參數為兩個int的可調用對象類型std::function<int(int, int)> Func;

使用之前需要導入<functional>庫,并且通過std命名空間使用。

其他函數實體轉化為std::function

std::function強大的地方在于,它能夠兼容所有具有相同參數類型的函數實體。

相比較于函數指針,std::function能兼容帶捕獲的lambda函數,而且對類成員函數提供支持。

#include <iostream>#include <functional>std::function<int(int, int)> SumFunction;// 普通函數int func_sum(int a, int b){  return a + b;}class Calcu{public:  int base = 20;  // 類的成員方法,參數包含this指針  int class_func_sum(const int a, const int b) const { return this->base + a + b; };  // 類的靜態成員方法,不包含this指針  static int class_static_func_sum(const int a, const int b) { return a + b; };};// 仿函數class ImitateAdd{public:  int operator()(const int a, const int b) const { return a + b; };};// lambda函數auto lambda_func_sum = [](int a, int b) -> int { return a + b; };// 函數指針int (*func_pointer)(int, int);int main(void) {  int x = 2;   int y = 5;  // 普通函數  SumFunction = func_sum;  int sum = SumFunction(x, y);  std::cout << "func_sum:" << sum << std::endl;  // 類成員函數  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;  // 類靜態函數  SumFunction = Calcu::class_static_func_sum;  sum = SumFunction(x, y);  std::cout << "Calcu::class_static_func_sum:" << sum << std::endl;  // lambda函數  SumFunction = lambda_func_sum;  sum = SumFunction(x, y);  std::cout << "lambda_func_sum:" << sum << std::endl;  // 帶捕獲的lambda函數  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;  // 仿函數  ImitateAdd imitate;  SumFunction = imitate;  sum = SumFunction(x, y);  std::cout << "imitate func:" << sum << std::endl;  // 函數指針  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

其中需要注意對于類成員函數,因為類成員函數包含this指針參數,所以單獨使用std::function是不夠的,還需要結合使用std::bind函數綁定this指針以及參數列表。

std::bind參數綁定規則

在使用std::bind綁定類成員函數的時候需要注意綁定參數順序:

// 承接上面的例子SumFunction = std::bind(&Calcu::class_func_sum, obj,     std::placeholders::_1, std::placeholders::_2);SumFunction(x, y);
  • 第一個參數為類成員函數名的引用(推薦使用引用)
  • 第二個參數為this指針上下文,即特定的對象實例
  • 之后的參數分別制定類成員函數的第1,2,3依次的參數值
  • 使用std::placeholders::_1表示使用調用過程的第1個參數作為成員函數參數
  • std::placeholders::_n表示調用時的第n個參數

看下面的例子:

// 綁定成員函數第一個參數為4,第二個參數為6SumFunction = std::bind(&Calcu::class_func_sum, obj, 4, 6);SumFunction(); // 值為 10// 綁定成員函數第一個參數為調用時的第一個參數,第二個參數為10SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_1, 10);SumFunction(5); // 值為 15// 綁定成員函數第一個參數為調用時的第二個參數,第一個參數為調用時的第二個參數SumFunction = std::bind(&Calcu::class_func_sum, obj, std::placeholders::_2, std::placeholders::_1);SumFunction(5, 10); // 值為 15

對于非類成員對象,一般直接賦值即可,會自動進行轉換并綁定參數,當然也可以使用std::bind指定參數綁定行為;

#include <iostream>#include <functional>// 按照順序輸出x, y, xvoid print_func(int x, int y, int z){  std::cout << x << " " << y << " " << z << std::endl;}std::function<void(int, int, int)> 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;}

需要注意:就算是綁定的時候指定了默認參數,在調用的時候也需要指定相同的參數個數(雖然不會起作用),否則編譯不通過。

關于回調函數

回調就是通過把函數等作為另外一個函數的參數的形式,在調用者層指定被調用者行為的方式。

通過上面的介紹,我們知道,可以使用函數指針,以及std::function作為函數參數類型,從而實現回調函數:

#include <iostream>#include <functional>std::function<int(int, int)> 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);}// 調用方式參考上面關于函數指針和std::function的例子

以上兩種方式,對于一般函數,簡單lambda函數而言是等效的。

但是如果對于帶有捕獲的lambda函數,類成員函數,有特殊參數綁定需要的場景,則只能使用std::function。

其實還有很多其他的實現回調函數的方式,如下面的標準面向對象的實現:

#include <iostream>// 定義標準的回調接口class ComputeFunc{public:  virtual int compute(int x, int y) const = 0;};// 實現回調接口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){  // 調用接口方法  return compute.compute(x, y);}// 調用方法如下int main(){  ComputeAdd add_func; // 創建一個調用實例  int sum = compute3(3, 4, add_func); // 傳入調用實例}

面向對象的方式更加靈活,因為這個回調的對象可以有很復雜的行為。

以上三種方法各有各的好處,根據你需要實現的功能的復雜性,擴展性和應用場景等決定使用。

另外,這些函數類型的參數可能為空,在調用之前,應該檢查是否可以調用,如檢查函數指針是否為空。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宣化县| 沛县| 丽水市| 美姑县| 黎平县| 太原市| 合川市| 南平市| 都江堰市| 会同县| 西青区| 白山市| 木兰县| 馆陶县| 芜湖市| 泗阳县| 维西| 富源县| 河津市| 高碑店市| 吴川市| 陵水| 集安市| 兴城市| 上杭县| 新营市| 麦盖提县| 精河县| 晋城| 长治县| 阿坝县| 和平县| 友谊县| 天峻县| 大冶市| 宾阳县| 合水县| 张掖市| 尼勒克县| 高雄县| 蒙阴县|