C++11中的包装器实战案例

2025-11-26 19:50
文章标签 c++ 实战 案例 包装

本文主要是介绍C++11中的包装器实战案例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《C++11中的包装器实战案例》本文给大家介绍C++11中的包装器实战案例,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧...

引言

在C++中,可调用对象类型五花八门(函数指针、仿函数、lambda表达式、类成员函数等),它们的逻辑功能十分相似,但是类型差异巨大,导致代码声明繁琐、接口适配困难、扩展性不足。而C++11通过引入std::function包装器,抹平了不同类型间的差异;引入std::bind包装器,灵活调整参数列表,以适配各种接口。

1.std::function

1.1.什么是std::function

std::function是C++11提供的类模板包装器,定义在<functional>头文件中,核心作用是消除不同可调用对象的类型差异。无论是普通函数、lambda还是仿函数,都能被std::function包装成同一类型。

template <class Ret, class... Args>
class function<Ret(Args...)>;
  • Ret:可调用对象的返回值类型
  • Args...:可调用对象的参数列表

1.2.核心用法

1.2.1.包装普通函数

普通函数是最基础的可调用对象:
代码示例:

#include <IOStream>
#include <functional>
using namespace std;
int add(int a, int b)
{
	return a + b;
}
int main()
{
	//包装add函数,类型匹配int<int, int>
	function<int(int, int)> func_add = add;
	cout << "1 + 2 = " << func_add(1, 2) << endl;
	return 0;
}

运行结果:

C++11中的包装器实战案例

1.2.2.包装仿函数

仿函数是重载operator()的类实例
代码示例:

#include <iostream>
#include <functional>
using namespace std;
struct Sub {
	int operator()(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	function<int(int, int)> func_sub = Sub();
	cout << "5 - 3 = " << func_sub(5, 3) << endl;
	return 0;
}

运行结果:

C++11中的包装器实战案例

1.2.3.包装lambda表达式

lambda是匿名函数对象,function能直接与之匹配
代码示例:

#include <iostream>
#includewww.chinasem.cn <functional>
using namespace std;
int main()
{
	function<int(int, int)> func_mul = [](int a, int b) {return a * b;};
	cout << "3 * 4 = " << func_mul(3, 4) << endl;
	return 0;
}

运行结果:

C++11中的包装器实战案例

1.2.4.包装类成员函数

包装类成员函数需要额外处理this指针,function需在参数列表中显式声明this对应的对象类型(指针或引用);
代码示例:

#include <iostream>
#include <functional>
using namespace std;
class Divide {
public:
	//非静态成员函数,参数隐含this指针
	double div(double a, double b)
	{
		return a / b;
	}
	//静态成员函数,不含this指针
	static double div_static(double a, double b)
	{
		return a / b;
	}
};
int main()
{
	Divide d;
	//包装非静态成员函数,包含this指针
	function<double(Divide*, double, double)> func_div = &Divide::div;//非静态成员函数必须取地址,不能隐式类型转换
	cout << "func_div: " << "12.2 / 2 = " << func_div(&d, 12.2, 2) << endl;
	//包装静态成员函数,不包含this指针
	function<double(double, double)> func_stat_div = Divide::div_static;//取地址可加可不加
	cout << "func_stat_div: " << "12.2 / 2 = " << func_stat_div(12.2, 2) << endl;
	return 0;
}

运行结果:

C++11中的包装器实战案例

1.2.5.空包装器

若std::function是空包装器(未绑定任何可调用对象),调用时会抛异常(std::bad_function_call),注意捕获。
代码示例:

#include <iostream>
#include <functional>
using namespace std;
int main()
{
	function<int(int, int)> func_empty;
	try {
		func_empty(3, 4);
	}
	catch (const bad_function_call& e) {
		cout << "error: " << e.what() << endl;
	}
	return 0;
}

运行结果:

C++11中的包装器实战案例

1.3.什么时候适合用std::function

  • 作为容器元素:如map<string,function<Arg...>>,实现字符串-函数映射
  • 编程为函数参数/返回值:传递回调函数时,不需要关注函数的具体类型(函数指针/仿函数/lambda),匹配函数签名(返回值类型、参数类型和数量)即可。
  • 简化类型声明:替代复杂的函数指针类型,如:int(*)(int ,int),代码更简洁。

2.std::bind

2.1.什么是std::bind

std::bind是C++11提供的函数模板,定义在<functional>头文件中,核心作用是调整可调用对象的参数列表(绑定固定参数、重新排序参数)

//simple(1)	
template <class Fn, class... Args>
  /* unspecified */ bind (Fn&& fn, Args&&... args);
//with return type (2)	
template <class Ret, class Fn, class... Args>
  /* unspecified */ bind (Fn&& fn, Args&&... args);

一般形式:auto newCallable = bind(callable, arg_list)

  • callable:原可调用对象
  • arg_list:参数列表
  • 参数列表中,placeholders::_n(如_1、_2)是占位符,表示新函数的第n个参数,未用占位符的参数会被“固定绑定”。

2.2.核心用法

2.2.1.绑定固定参数(减少参数)

假设我们有一个计算乘法的函数mul,如果我们想固定其中一个因数a为100, 只用输入另一个因数b,此时可用bind实现
代码示例:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
int mul(int a, int b)
{
	return a * b;
}
int main()
{
	//绑定_a == 100, 新函数第一个参数_1传给b
	auto mul_fixed_a = bind(mul, 100, _1);
	cout << "100 * 5 = " << mul_fixed_a(5) << endl;
	////绑定_b == 200, 新函数第一个参数_1传给a
	auto sub_fixed_b = bind(mul, _1, 200);
	cout << "3 * 200 = " << mul_fixed_a(3) << endl;
	return 0;
}

运行结果:

C++11中的包装器实战案例

2.2.2.重排参数顺序

如果要调整函数参数顺序,可以通过调整占位符顺序实现
代码示例:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
int sub(int a, int b)
{
    return a - b;
}
int main() {
    // 原函数是 a - b,重排后变成 b - a(_2 是新函数第2个参数,_1 是第1个)
    auto sub_swap = bind(sChina编程ub, _2, _1);
    cout << "5 - 10 = " << sub_swap(10, 5) << endl; // 输出:(5-10)*10=-50
    return 0;
}

运行结果:

C++11中的包装器实战案例

2.2.3.结合成员函数使用

非静态成员函数中参数包含隐藏的this指针,因此bind绑定时需要包含该类的引用或指针参数
代码示例:

#include <iostream>
#include <functional>
using namespace std;
using namespace placeholders;
class Calculator {
public:
    double multiply(double a, double b) {
        return a * b;
    }
};
int main() {
    Calculator calc;
    // 绑定 calc 作为 this 指针,_1 和 _2 是新函数的两个参数
    auto calc_mul = bind(&Calculator::multiply, &calc, _1, _2);
    cout << "3 * 4 = " << calc_mul(3, 4) << endl; // 输出:12
    return 0;
}

运行结果:

C++11中的包装器实战案例

2.3.什么时候适合用std::bind

  • 适配参数不匹配的接口:比如一个第三方库函数需要传三个参数,但我的函数只有两个参数,这时可以用bind绑定一个参数,即可适配
  • 简化重复调用:若某函数频繁用相同参数调用,可以用bind绑定,简化函数

3.实战案例

3.1.逆波兰表达式求值(function + map优化版本)

function + map --> “操作符 - 函数”映射
代码示例:

#include <stack>
#include <vector>
#include <string>
#include <functional>
#include <map>
using namespace std;
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        // 用 map 存储“操作符-function”映射
        map<string, function<int(int, int)>> op_map = {
            {"+", [](int a, int b) { return a + b; }},
            {"-", [](int a, int b) { return a - b; }},
            {"*", [](int a, int b) { return a * b; }},
            {"-", [](int a, int b) { return a - b; }},
            {"/", [](int a, int b) { return a / b; }},
            {"%", [](int a, int b) { return a % b; }}
        };
        for (auto& str : tokens) {
            if (op_map.count(str)) { // 匹配到操作符
                int right = st.top(); st.pop();
                int left = st.top(); st.pop();
                // 调用对应的 function
                st.push(op_map[str](left, right));
            }
            else {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

3.2.复利计算

在计算复利时,需要传入三个参数:利率、本金、年限,如果我们想固定利率和年限,只输入本金,则可用bind简化函数。
代码示例:

#include <functional>
#include <iostream>
using namespace std;
using namespace placeholders;
int main() {
    // 复利计算 lambda:rate=年利率,money=本金,year=年限,返回利息
    auto calc_interest = [](double rate, double money, int year) -> double {
        double ret = money;
        for (int i = 0; i < year; i++) {
            ret += ret * rate; // 每年复利
        }
        return ret - money; // 利息 = 最终金额 - 本金
    };
    // 用 bind 固定利率和年限,生成专用函数(只需传入本金)
    function<double(double)> interest_3y_1_5 = bind(calc_interest, 0.015, _1, 3);  //android 3年1.5%利率
    function<double(double)> interest_5y_2_0 = bind(calc_interest, 0.020, _1, 5);  // 5年2.0%利率
    function<double(double)> interest_10y_2_5 = bind(calc_interest, 0.025, _1, 10); // 10年2.5%利率
    // 调用简化版函数
    cout << "100万 3年1.5%利息:" << interest_3y_1_5(1000000) << endl; // 约45678.38
    cout << "100万 5年2.0%利息:" << interest_5y_2_0(1000000) << endl; // 约104080.80
    cout << "100万 10年2.5%利息:" << interest_10y_2_5(1000000) << endl; // 约280084.50
    return 0;
}

运行结果:

C++11中的包装器实战案例

4.注意事项

  • function非静态成员函数时,由于非静态成员函数参数中隐藏了this指针,因此function参数列表必须包含this指针对应的对象类型(指针或引用),否则编译报错。
  • bind占位符在命名空间std::placeholders中,使用前记得显式声明using namespace std::placeholders
  • 若用bind绑定引用参数,需用ref()cref()包装,否则bind会默认按值传递。
void print(int& x) { x++; cout << x << endl; }
int main() {
    int a = 10;
    auto func = bind(print, ref(a)); // 用 ref 绑定引用
    func(); // 输出 11,a 被修改为 11
    return 0;
}
  • 包装器底层是函数对象封装,无额外性能消耗,不必担心效率问题。

结语

std::function 和 std::bind 作为 C++11 引入的强大工具,共同为处理复杂的函数调用场景带来了革命性的简化:它们消除了不同可调用对象(如普通函数、Lambda 表达式、成员函数)之间的类型壁垒,使得函数的传递、存储和调用变得前所未有的统一和灵活。开发者无需再为每种回调类型定义繁琐的函数指针或适配器,std::function 提供了一个通用的 “容器”,而 std::bind 则像一个万能的 “适配器”,能够轻松调整函数签名,固定参数或重排参数顺序,极大地提升了代码的表达力和可维护性。

到此这篇关于C++11中的包装器的文章就介绍到这了,更多相关C++ 包装器内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于C++11中的包装器实战案例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1156287

相关文章

C++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

C++构造函数中explicit详解

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以... 目录1. 什么是explicit2. 隐式转换的问题3.explicit的使用示例基本用法多参数构造

Springboot3 ResponseEntity 完全使用案例

《Springboot3ResponseEntity完全使用案例》ResponseEntity是SpringBoot中控制HTTP响应的核心工具——它能让你精准定义响应状态码、响应头、响应体,相比... 目录Spring Boot 3 ResponseEntity 完全使用教程前置准备1. 项目基础依赖(M

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias

Java 队列Queue从原理到实战指南

《Java队列Queue从原理到实战指南》本文介绍了Java中队列(Queue)的底层实现、常见方法及其区别,通过LinkedList和ArrayDeque的实现,以及循环队列的概念,展示了如何高效... 目录一、队列的认识队列的底层与集合框架常见的队列方法插入元素方法对比(add和offer)移除元素方法

Spring Boot基于 JWT 优化 Spring Security 无状态登录实战指南

《SpringBoot基于JWT优化SpringSecurity无状态登录实战指南》本文介绍如何使用JWT优化SpringSecurity实现无状态登录,提高接口安全性,并通过实际操作步骤... 目录Spring Boot 实战:基于 JWT 优化 Spring Security 无状态登录一、先搞懂:为什

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

C++多线程开发环境配置方法

《C++多线程开发环境配置方法》文章详细介绍了如何在Windows上安装MinGW-w64和VSCode,并配置环境变量和编译任务,使用VSCode创建一个C++多线程测试项目,并通过配置tasks.... 目录下载安装 MinGW-w64下载安装VS code创建测试项目配置编译任务创建 tasks.js