C++11新特性(下)----《Hello C++ Wrold!》(26)--(C/C++)

C++11新特性(下)----《Hello C++ Wrold!》(26)--(C/C++)

文章目录

前言

在 C++11 标准带来的诸多革命性特性中,“简化代码编写” 与 “统一可调用对象管理” 是两大核心目标。lambda 表达式解决了传统仿函数 “定义繁琐、复用性低” 的痛点,让局部场景下的自定义逻辑(如排序规则、回调函数)能以更简洁的匿名函数形式实现;可变参数模板则打破了模板参数数量固定的限制,为 STL 容器(如emplace_back)和通用函数设计提供了灵活的参数处理能力;而 function 包装器与 bind 函数,则进一步整合了函数指针、仿函数、lambda 等不同类型的可调用对象,实现了统一管理与参数适配,甚至让可调用对象存储到容器中成为可能。

这些特性并非孤立存在 ——lambda 的底层依赖仿函数实现,可变参数模板为emplace系列接口提供了技术支撑,function 与 bind 则基于前两者的特性,解决了 “不同可调用对象类型不统一” 的问题。本文将从实际开发需求出发,先讲解 lambda 表达式的语法规则与捕获逻辑,再深入可变参数模板的展开方法与应用场景,最后通过 function 包装器与 bind 函数的实例,展示如何统一管理各类可调用对象、灵活调整参数顺序与固定参数值。

文中不仅会拆解易混淆的概念(如 lambda 的 mutable 修饰符作用、placeholders 占位符的使用规则),还会结合具体代码示例(如用 vector 存储 function 对象、用 bind 适配类成员函数),帮助读者理解这些特性的设计逻辑与实际价值。同时,文末的作业解析也将围绕 C++11 常见考点(如范围 for 的适用场景)展开,进一步巩固对标准特性的理解,为后续高效使用 C++11 进行开发打下基础。

lambda表达式

比如:一个类里面有很多成员变量,想对他们都进行排序,但是每次比较的逻辑不一样,而且还要每次都写一个类去比较,太麻烦了,所以就引入了lambda表达式

lambda表达式是局部的匿名的函数对象–所以sort填仿函数那里可以写这个

底层其实是用的仿函数实现的
这个表达式不存在重载这个说法哈

引申:auto f1 = (){}; 和auto f2 = (){}; f1和f2也不是同一类型 f1=f2的话会报错

也就是. lambda表达式之间不能相互赋值,即使看起来类型相同
这个表达式的格式:[捕捉列表](参数列表)(mutable)-> 返回值类型 { 函数体 }; 捕获列表的话可以捕获这个表达式所在域的局部变量--函数也可以捕捉哈 --全局域里的不用捕获也能用 --这种的强行捕获可能会报错 参数列表里面不传参的话,()也可以省了 这个mutable的话可以取消捕获列表里面东西默认的const性 但是在用该修饰符的时候,参数列表不能省略 --如果捕获的东西本来是const的话,加了mutable也变不了 注意:捕获列表默认是const的 ->返回值类型:这个一般可以不用写,编译器会推导 
关于捕获列表:(没用mutable的话,捕获的东西都不能被修改)

[var]:表示值传递方式捕捉变量var–var的类型会跟传递过来的一模一样

[=]:表示值传递方式捕获所有父作用域中的变量(包括this)

[&var]:表示引用传递捕捉变量var eg:[&x,&y]--这个跟取地址不要混了

[&]
:表示引用传递捕捉所有父作用域中的变量(包括this)

这里面的&=:如果是[&,x]那就只有x是值传递,其他都是引用

注意:捕捉列表不允许变量重复传递,否则就会导致编译错误[=,a]就不行,a捕获了两次
用法展示:
引申:可调用对象的四个存储方法:函数指针,仿函数,lambda表达式,用包装器搞到容器里面

可变参数模板

就是让模板参数可以是不确定的数目

其实日常自己写的话很少用的
template<class... Args>voidShowList(Args... args){} Args表示一个包含零个或多个类型的参数包 args也是参数包,里面可以是0或多个参数 ...是展开参数包的操作(放在参数包后面--除了模板参数声明那里) 但是这个参数包想知道里面的参数是啥的话比较困难--不支持eg:arg[1]这样 

展开参数包的方法

1.递归展开参数包

2.逗号表达式展开参数包

应用

假设有个日期类Date template<class... Args> Date*Create(Args... args){ Date* ret =newDate(args...);return ret;} 可以Date* p1 =Create(2025,9,7);这样来用 或者 Date d(2025,9,7); Date* p1 =Create(d);也行 
emplace_back有可变参数模板 push_back没用 emplace_back可以直接传零散的参数进去 但是push_back必须要先把零散的参数搞成临时对象再传进去 

包装器

fiction包装器

function包装器也叫作适配器 他需要头文件#include <functional>

作用:比如:

并且包装器可以让可调用对象存储到容器中去了
用法: function的类模板原型:template<classT> function;//这是模板的声明template<classRet,class... Args>classfunction<Ret(Args...)>; Ret: 被调用函数的返回类型 Args…:被调用函数的形参 使用:doublef(double i){return i /2;}structFunctor{doubleoperator()(double d){return d /3;}}; vector<function<double(double)>> v ={ f,[](double d)->double{return d /4;},Functor()}; 第一个double是返回值类型 第二个double是形参类型--两个double类型的形参的话就写(double,double)
用法还有eg: map<string,function<int(int)>> map1 ={{"a",[](int x){return x;}},{"y",[](int x){return x+1;}}}; 用的话就:int x =0;int a = map1["a"](x);//注意理解map1["a"]取出包装器.....

bind函数

这个的头文件也是#include <functional>

bind
是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表

这个的作用也就是把参数的顺序改了+可以固定一些参数的值
这里固定参数的值跟直接给缺省值的比较:

缺省值只能给固定的参数一个固定值

这里就不一样了
用法: 对于普通的函数:doublePlus(int a,int b,double rate){return(a + b)* rate;} function<double(int,int)> Plus1 =bind(Plus, placeholders::_2, placeholders::_1,4.0); 这个的话就是rate固定是4.0 然后Plus1(9,8) 这里的placeholders::_1表示的是(9,8)里面的9然后会传给b这样 如果想固定的值在中间的话 eg:doublePlus(int a,double rate,int b){return(a + b)* rate;} function<double(int,int)> Plus1 =bind(Plus, placeholders::_2,4.0,placeholders::_1);Plus1(9,8);--8最终给了a 这个依旧是_1,_2这样的哈 
用法: 对于类里面的成员函数的话: 如果函数是静态的,那到没啥区别(但是记得加上类域) 如果函数不是静态的: 函数后面要跟上这个对象或者对象的指针才行 --函数是静态的话,可以加那个也可以不加 eg:classSubType{public:intsub(int a,int b){return a - b;}}; SubType st; function<double(int,int)> Sub2 =bind(&SubType::sub,&st, placeholders::_1, placeholders::_2,3); function<double(int,int)> Sub2 =bind(&SubType::sub, st, placeholders::_1, placeholders::_2,3); function<double(int,int)> Sub3 =bind(&SubType::sub,SubType(), placeholders::_1, placeholders::_2,3);//这里用的是匿名对象 引申:类里面的函数在外面用的时候必须要到类域里面去找才行 

作业部分

在这里插入图片描述
下面关于范围for说法错误的是(C) A.范围for可以直接应用在数组上 B.对于STL提供的所有容器,均可以使用范围for依次访问器元素 C.使用范围for操作stack,可以简化代码 //上面说的是容器,但是stack是容器适配器 D.对于自定义类型,想要支持范围for,必须提供begin和end迭代器 E.范围for编译器最终是将其转化为迭代器来进行处理的 
在这里插入图片描述

Read more

C++之基于正倒排索引的Boost搜索引擎项目介绍

C++之基于正倒排索引的Boost搜索引擎项目介绍

1. 为什么我们要写项目 1.1 把理论知识转化为实际能力 计算机专业的课程(如编程语言、数据结构、算法、操作系统等)多以理论和抽象概念为主,而项目是 “用起来” 的过程。比如学了数据结构中的链表、树,只有在做通讯录管理系统、文件索引工具等项目时,才能真正理解它们在实际场景中的作用;学了多线程,在开发简单的并发程序(如聊天软件)时,才会明白锁机制、线程同步的实际意义。 1.2 培养解决复杂问题的思维 项目开发中总会遇到各种 “意料之外” 的问题:代码逻辑漏洞、性能瓶颈、兼容性问题等。解决这些问题的过程,能倒逼你学会拆解问题、查阅文档(如官方手册、技术博客)、调试代码(用 IDE 断点、日志分析),甚至借鉴开源项目的思路 —— 这些都是未来工作中不可或缺的 “实战思维”。 1.3 理解完整的开发流程

By Ne0inhk
STL?vector!!!

STL?vector!!!

一、前言         之前我们借助手撕string加深了类和对象相关知识,今天我们将一起手撕一个vector,继续深化类和对象、动态内存管理、模板的相关知识 二、vector相关的前置知识         1、什么是vector?         vector是一个STL库中提供的类模板,它是存储元素对象的顺序表,其中提供了一些有关增删查改的接口,它的特点是可以通过下标的方式在表中的任意位置进行读、写         2、vector中的相关接口         在本文接下来的部分会介绍vector的常用接口,事实上借助这些接口就可以解决平常所能遇到的大部分问题,如果还需要了解vector提供的更多接口及使用方法的话,可以跳转到一下网页:         vector - C++ Referencehttps://legacy.cplusplus.com/reference/vector/vector/?kw=vector 三、手撕一个vector类         1、成员变量与整体框架         注意:之前的顺序表我们都是通过记录指针、元素个数和空

By Ne0inhk
2020年信奥赛C++提高组csp-s初赛真题及答案解析(选择题11-15)

2020年信奥赛C++提高组csp-s初赛真题及答案解析(选择题11-15)

2020年信奥赛C++提高组csp-s初赛真题及答案解析(选择题11-15) 第 11 题:小明想通过走楼梯来锻炼身体,假设从第 1 层走到第 2 层消耗 10 卡热量,接着从第 2 层走到第 3 层消耗 20 卡热量,再从第 3 层走到第 4 层消耗 30 卡热量,依此类推,从第 k 层走到第 k+1 层消耗 10k卡热量 (k>1)。如果小明想从 1 层开始,通过连续向上爬楼梯消耗 1000 卡热量,至少要爬到第几层楼? ( )。 A. 14 B. 16

By Ne0inhk
【C++】手搓一个STL风格的vector容器

【C++】手搓一个STL风格的vector容器

@TOC(手搓一个STL风格的vector容器) 手搓一个STL风格的vector容器 github地址 有梦想的电信狗 0. 前言:动态数组的工程实践 在C++标准库中,vector容器作为最核心的序列式容器,其设计融合了动态数组的高效性与安全性。本文将通过完整实现一个简化版vector容器,重点剖析迭代器失效、深拷贝控制、动态扩容等关键问题。代码实现将保持与STL兼容的接口设计。 有了之前实现string的经验,我们实现vector也就相对容易了。 1. 基础架构设计 1.1 成员变量与迭代器 * 为了和标准库中的vector区分,我们把自己实现的vector封装在m_vector这个命名空间中 * vector的底层是就是顺序表,采用顺序表的结构来实现即可,重点掌握和STL中的顺序表普通的顺序表的实现有哪些不同 * 基本结构如下: namespace m_vector {template<classT>classvector{public://将原生指针封装为迭代器typedef T* iterator;typedefconst T* const

By Ne0inhk