深入理解C++ 中的可调⽤对象

ops/2024/9/24 0:20:44/

C++中的可调⽤对象总结

  • 普通函数
  • 类成员函数
  • 类静态成员函数
    • 与类成员函数的区别
  • 仿函数
    • 简单示例
    • 高级用法-状态保持
    • 优缺点
      • 优点
      • 缺点
  • 函数指针
    • 获取函数地址
    • 声明并调用函数指针
  • lambda表达式
    • 语法定义
    • 捕获
      • 单个捕获符
  • std::function()
  • 协程

可调用对象用处⼴泛:

  • ⽐如在使⽤⼀些基于范围的模板函数时,如 sort(It first, It last, Compare cmp)all_of()find_if() 等),需要传⼊⼀个可调⽤对象客制化处理。
  • 在处理⼀些回调函数、触发函数时,也会使⽤可调⽤对象。

满足以下条件的为可调用对象:

  • 是一个函数或类成员函数
  • 是一个函数指针
  • 是一个重载operator()的类对象
  • lambda表达式
  • 是一个可转型为函数指针的类对象
  • 是一个类成员函数指针
  • bind表达式、std::function()
  • 协程

普通函数

int add(int a, int b) {return a + b;
}
int main() {int num1, num2;std::cout << "Enter two numbers: ";std::cin >> num1 >> num2;int sum = add(num1, num2);std::cout << "The sum of " << num1 << " and " << num2 << " is: " << sum << s << "\n";return 0;
}

类成员函数

#include <iostream>
using namespace std;
class Box{
public:double length; // ⻓度double breadth; // 宽度double height; // ⾼度// 成员函数声明double getVolume(void);void setLength( double len );void setBreadth( double bre );void setHeight( double hei );
};
// 成员函数定义
double Box::getVolume(void)
{return length * breadth * height;
}
void Box::setLength( double len )
{length = len;
}
void Box::setBreadth( double bre )
{breadth = bre;
}
void Box::setHeight( double hei )
{height = hei;
}
int main( )
{Box box; // 声明 box,类型为 Boxdouble volume = 0.0; // ⽤于存储体积// 详述box.setLength(6.0);box.setBreadth(7.0);box.setHeight(5.0);// 体积volume = box.getVolume();cout << "box 的体积:" << volume <<endl;return 0;
}

类静态成员函数

类中的静态成员函数作⽤在整个类的内部,类静态成员函数属于类而非对象。静态成员函数只能访问对应类内部的静态数据成员(因为静态成员函数没有this指针)。类的static函数在类内声明、类外定义时,类内使用static修饰,类外则不能加static关键字,否则会出现编译错误。

class Box{
private:int _non_static;static int _static;
public:int a(){return _non_static;}static int b(){//_non_static=0; 错误//静态成员函数不能访问⾮静态成员变量return _static;}static int f(){//a(); (不对,静态成员函数不能访问⾮静态成员函数)return b();}
};int Box::_static= 0;// static静态成员变量可以在类的外部修改int main(){Box box;Box* pointer=&box;box.a();pointer->a();Box::b(); // 类名::静态成员函数名return 0;
}

与类成员函数的区别

  • 静态成员函数没有 this 指针,只能访问静态成员(包括静态成员变量和静态成员函数)。
  • 普通成员函数有 this 指针,可以访问类中的任意成员;⽽静态成员函数没有 this 指针。

仿函数

仿函数就是重载了()运算符的类对象,函数功能通过重载()实现。(⾏为类似函数,故称仿函数)。实际上就是创建⼀个类,该类重载了()运算符,使得类的实例可以像函数⼀样被调⽤。这允许你在函数对象内部保存状态,并在调⽤时执⾏操作。

简单示例

class Foo
{void operator()(){cout << __FUNCTION__ << endl;}
};
int main()
{Foo a;//定义对象调⽤a.operator()();//直接通过对象调⽤a();//通过临时对象调⽤Foo()();
}

高级用法-状态保持

仿函数可以具有成员变量,因此在多次调用之间可以保持状态。这在算法需要记录或更新某些值的情况下非常有用。

考虑一个例子:对一个vector统计长度小于5的string的个数

优缺点

优点

  • 状态保持:仿函数可以具有成员变量,因此在多次调用之间可以保持状态。
  • 灵活的接口设计:仿函数可以根据需要定制接口,以适应特定的算法或场景。例如,可以根据算法需要添加额外的成员函数或数据成员。
  • 更好的封装:仿函数可以将数据和操作封装在一个单独的对象中,这有助于实现更清晰、更模块化的代码。
  • 缺点

  • 需要单独实现一个类

函数指针

与数据项相似,函数也有地址。函数的地址就是存储它的机器语⾔代码内存的开始地址。通常情况下,这些地址对⽤⼾⽽⾔并不重要,但对于程序⽽⾔,却很有⽤。⽐⽅说,可以编写将另⼀个函数的地址作为参数的函数,这样第⼀个函数就能够找到第⼆个函数,并运⾏它。与直接调⽤另⼀个函数相⽐,这种⽅法虽然很笨拙,但是它允许在不同的时间传递不同函数的地址,这也就意味着可以在不同的时间使⽤不同的函数。

获取函数地址

函数名,不加()

声明并调用函数指针

使用using而非old style声明函数指针!
注意两种方式绑定函数的区别!

  1. 使用 using 声明函数指针
#include<iostream>int add(int a, int b) {return a + b;
}int main() {// 使用 using 声明函数指针类型using AddFunctionPtr = int (*)(int, int);// 创建函数指针变量并指向 add 函数AddFunctionPtr my_add_function = &add;// 使用函数指针调用 add 函数int result = my_add_function(3, 4);return 0;
}
  1. old style
#include<iostream>int add(int a, int b) {return a + b;
}int main() {// 使用 old style 声明函数指针int (*my_add_function )(int, int);// 指向 add 函数my_add_function = &add;// 使用函数指针调用 add 函数int result = my_add_function(3, 4);// int result = (*my_add_function)(3, 4);	// okreturn 0;
}

lambda表达式

Lambda表达式是Modern C++的⼀个语法糖 。 Lambda表达式用于构造闭包:能够捕获作用域中的变量的无名函数对象。

语法定义

  1. 没有显式模板形参的 lambda 表达式(可以不泛型)
  [捕获列表]     [前属性]       [形参列表] [说明符]   [异常]         [后属性]        [返回类型]         [约束]       [函数体]
[capture list] front-attr(opt) (params) specs(opt)exception(opt) back-attr(opt) railing-type(opt) requires(opt) {body}
  1. 有显式模板形参的 lambda 表达式(必然泛型)(C++20起)

捕获

捕获 是一个含有>=0个捕获符 的逗号分隔列表,可以默认捕获符 开始。捕获列表定义了可以从 lambda 函数体之内访问的外部变量。默认捕获符只有&(按引用) 和 =(按复制),它们都将隐式捕获被使用的自动存储期变量(函数能看到的任一变量)。

注意:以默认捕获符隐式捕获的当前对象(*this),将始终按引用捕获,即使默认捕获符是 =

单个捕获符

  1. 简单的按复制捕获
  2. 作为包展开的简单的按复制捕获
  3. 带初始化器的按复制捕获
  4. 简单的按引用捕获
  5. 作为包展开的简单的按引用捕获
  6. 带初始化器的按引用捕获
  7. 当前对象的简单的按引用捕获
  8. 当前对象的简单的按复制捕获
  9. 初始化器为包展开的按复制捕获
  10. 初始化器为包展开的按引用捕获
  • 不可重复捕获
    • 当默认捕获符是 & 时,后继的简单捕获符不能以 & 开始

      struct S2 { void f(int i); };
      void S2::f(int i)
      {[&]{};          // OK:默认按引用捕获[&, i]{};       // OK:按引用捕获,但 i 按值捕获[&, &i] {};     // 错误:按引用捕获为默认时的按引用捕获[&, this] {};   // OK:等价于 [&][&, this, i]{}; // OK:等价于 [&, i]
      }
      
    • 当默认捕获符是 = 时,后继的简单捕获符必须以 &、*this (C++17 起)、this (C++20 起) 之一开始。

      struct S2 { void f(int i); };
      void S2::f(int i)
      {[=]{};        // OK:默认按复制捕获[=, &i]{};    // OK:按复制捕获,但 i 按引用捕获[=, *this]{}; // C++17 前:错误:无效语法// C++17 起:OK:按复制捕获外围的 S2[=, this] {}; // C++20 前:错误:= 为默认时的 this// C++20 起:OK:同 [=]
      }
      
    • 任何捕获符只可以出现一次,并且名字不能与任何形参名相同:

      struct S2 { void f(int i); };
      void S2::f(int i)
      {[i, i] {};        // 错误:i 重复[this, *this] {}; // 错误:"this" 重复(C++17)[i] (int i) {};   // 错误:形参和捕获的名字相同
      }
      

std::function()

协程


http://www.ppmy.cn/ops/56974.html

相关文章

01 Web基础与HTTP协议

1.1 Web 基础 1.1.1 域名和 DNS 1. 域名的概念 网络是基于TCP/IP协议进行通信和连接的,每一台主机都有一个唯一的标识(固定的IP地址),用以区别在网络上成千上万个用户和计算机。网络在区分所有与之相连的网络和主机时,均采用一种唯一、通用的地址格式,即每一个与网络相连接的…

Python数据分析实战,公交车站点设置优化分析,案例教程编程实例课程详解

一、引言 随着城市化进程的加快,公共交通在城市交通中扮演着越来越重要的角色。公交车站点作为公共交通系统的重要组成部分,其布局设计直接影响到公共交通的运输效率和市民的出行体验。本文将通过Python数据分析的方法,对某城市的公交车站点设置进行优化分析,旨在提出合理的…

【YOLOv8改进[注意力]】添加iRMB倒置残差块注意力(2023.8) + 含全部代码和详细修改方式 + 手撕结构图

本文将进行在YOLOv8中使用iRMB倒置残差块注意力 的实践,助力YOLOv8目标检测效果的实践,文中含全部代码、详细修改方式以及手撕结构图。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 iRMB 二 使用iRMB倒置残差块注意力

在C++中怎样使用C库

在C中使用C库是一个相对直接的过程&#xff0c;因为C是从C发展而来的&#xff0c;并且与C高度兼容。这意味着你可以直接在C代码中使用C库函数、头文件和变量&#xff0c;而无需进行特殊转换。以下是一些基本的步骤和注意事项&#xff0c;用于在C中使用C库&#xff1a; 1. 包含…

Redis服务器统计和配置信息简介

Redis服务器统计和配置信息简介 首先使用INFO命令在Redis中用于获取Redis服务器的各种统计和配置信息;执行上述命令后&#xff0c;返回的信息分为多个部分&#xff0c;包括服务器信息、客户端信息、内存信息、持久化信息、统计信息、复制信息、CPU信息和键空间信息&#xff1b;…

LruCache、Glide和SmartRefreshLayout使用总结

&#xff08;一&#xff09;Android智能下拉刷新框架-SmartRefreshLayout https://github.com/scwang90/SmartRefreshLayout?tabreadme-ov-file &#xff08;二&#xff09;LruCache使用 使用它可以进行图片的内存缓存 public class ImageLoaderUtil {private LruCache<St…

数据库之MQL

1&#xff0c;查询所有 mysql> select * from grade;2&#xff0c; mysql> select id,firstname,lastname from grade;3&#xff0c; mysql> select firstname,lastname from grade where id > 4;4&#xff0c; mysql> select * from grade where sex f;5&…

架构师机器学习操作 (MLOps) 指南

MLOps 是机器学习操作的缩写&#xff0c;是一组实践和工具&#xff0c;旨在满足工程师构建模型并将其投入生产的特定需求。一些组织从一些自主开发的工具开始&#xff0c;这些工具在每次实验后对数据集进行版本控制&#xff0c;并在每个训练周期后对检查点模型进行版本控制。另…