【C/C++ 13】C++11高效特性

目录

一、初始化列表

二、auto

三、decltype

四、可变参数列表

五、lambda表达式


C++11在C++98的基础上增添了许多特性,但是同时也使得C++程序的开发变得复杂繁琐,让众多开发者苦不堪言,于是我们需要从C++11新增舔的特性中选择一些能够提高开发效率的东西进行学习和应用。

一、初始化列表

在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定,比如:

struct Point
{int _x;int _y;
};int main()
{int array1[] = { 1, 2, 3, 4, 5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0;
}

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自
定义的类型,使用初始化列表时,可添加等号(=),也可不添加,但是这样一来,C++代码就有些显得花里胡哨了,为了和C++98更兼容,建议在对列表进行初始化时都加上等号。

但是C++11的初始化列表在new表达式中非常方便,我们只在new表达式中使用C++11初始化列表即可。

#include <iostream>
using namespace std;int main()
{int* arr1 = new int[3]{ 0 };int* arr2 = new int[3]{ 1, 2, 3 };for (int i = 0; i < 3; ++i)cout << arr1[i] << ' ';// 0 0 0cout << endl;for (int i = 0; i < 3; ++i)cout << arr2[i] << ' ';// 1 2 3return 0;
}
#include <iostream>
using namespace std;int main()
{auto il = { 10, 20, 30 };cout << typeid(il).name() << endl;// class std::initializer_list<int>return 0;
}
// std::initializer_list
// This type is used to access the values in a C++ initialization list, 
// which is a list of elements of type const T.// std::initializer_list一般是作为构造函数的参数。
// C++11对STL中的不少容器就增加std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。
// 也可以作为operator = 的参数,这样就可以用大括号赋值。
#include <iostream>
#include <vector>
using namespace std;int main()
{// 初始化列表对STL容器初始化构造vector<int> arr = { 1, 2, 3 };for (int i = 0; i < arr.size(); ++i)cout << arr[i] << ' ';// 1 2 3return 0;
}

二、auto

C++11中 auto 用于实现自动类型推断,常应用于迭代器范围for循环中。

#include <iostream>
#include <map>
using namespace std;int main()
{int i = 10;auto p = &i;cout << typeid(p).name() << endl;	// int *map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"}, {"delete", "删除"} };//map<string, string>::iterator it = dict.begin();auto it = dict.begin();		// 迭代器类型while (it != dict.end()){cout << "<" << it->first << ", " << it->second << ">" << ' ';	// delete, 删除++it;}cout << endl;// 范围for循环for (const auto& e : dict)cout << "<" << e.first << ", " << e.second << ">" << ' ';	// delete, 删除return 0;
}

三、decltype

关键字decltype将变量的类型声明为表达式指定的类型。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;template<class T1, class T2>
void F(T1 x, T2 y)
{decltype(x * y) ret;cout << typeid(ret).name() << endl;
}int main()
{const int x = 1;double y = 2.5;decltype(x * y) ret;decltype(&x) p;cout << typeid(ret).name() << endl;		// doublecout << typeid(p).name() << endl;		// int const *F(1, 'a');		// intreturn 0;
}

四、可变参数列表

C++11的可变参数模板能够创建可以接受可变参数的函数模板和类模板,相比于C++98类模板只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}/*
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,
它里面包含了0到N(N >= 0)个模版参数。我们无法直接获取参数包args中的每个参数的,
只能通过展开参数包的方式来获取参数包中的每个参数,
这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。
由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。
*/
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;// 递归方式展开参数包
template<class T>
void ShowList(const T& value)
{cout << value << endl;
}template <class T, class ...Args>
void ShowList(T value, Args... args)
{cout << value << " ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 'a');ShowList(1, 'a', string("test"));return 0;
}
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;// 逗号表达式展开参数包
template<class T>
void PrintArgs(T t)
{cout << t << ' ';
}template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArgs(args), 0)... };cout << endl;
}int main()
{ShowList(1);ShowList(1, 'a');ShowList(1, 'a', string("test"));return 0;
}/*这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的,PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式,我们知道逗号表达式会按顺序执行逗号前面的表达式。expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行PrintArg(args),再得到逗号表达式的结果0。同时还通过初始化列表来初始化一个变长数组,{(PrintArg(args), 0)...}将会展开成((PrintArg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... )最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]。于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分PrintArg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。
*/

五、lambda

C++11的 lambda 表达式,实际上是一个匿名函数,熟练使用 lambda 表达式,可以极大地提升编程效率。

// lambda表达式语法
[capture-list](parameters)mutable->return-type{statement}// [capture-list]:捕捉列表,捕捉上下文中的变量供lambda使用
// (parameters):参数列表
// mutable:取消lambda函数的默认常性,使用该修饰符时参数列表不可省略
// ->return-type:返回值类型,可省略由编译器推导
// {statement}:函数体,除了可以使用参数列表的变量,还可以使用捕捉到的变量

在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
using namespace std;int main()
{// 最简单的lambda表达式auto func1 = [] {};func1();// 省略参数列表,返回值由编译器推导int a = 3, b = 4;auto func2 = [=] {cout << a + b << endl; };func2();auto func3 = [&](int c) {b = c + a; };func3(10);cout << "b = " << b << endl;cout << [=, &b](int c)->int {return b += a + c; }(10) << endl;auto func4 = [b](int a)mutable {b %= 10; return b + a; };cout << func4(5) << endl;return 0;
}/*
捕获列表说明:1. [var]:表示值传递方式捕捉变量var2. [=]:表示值传递方式捕获所有父作用域中的变量(包括this)3. [&var]:表示引用传递捕捉变量var4. [&]:表示引用传递捕捉所有父作用域中的变量(包括this)5. [this]:表示值传递方式捕捉当前的this指针注意:1. 父作用域指包含lambda函数的语句块2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割3. 捕捉列表不允许变量重复传递,否则就会导致编译错误4. lambda表达式之间不能相互赋值
*/

从原理上看,编译器对于lambda表示式的实现方式,就是将其作为仿函数实现的,而仿函数的实现原理就是对 operator() 进行重载。也就是说,如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。 

从代码编写角度来说,使用lambda表达式效率更高。

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
using namespace std;class Salary
{
public:Salary(int money): _money(money){}int operator()(int month){return _money * month;}private:int _money;
};int main()
{int money = 3000;int month = 5;Salary a(money);cout << a(month) << endl;auto func = [=] {return money * month; };cout << func() << endl;
}

六、包装器

function 包装器也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
using namespace std;template<class F, class T>
T UseF(F f, T t)
{static int count = 0;cout << "count: " << ++count << endl;cout << "count: " << &count << endl;return f(t);
}double func(double x)
{return x / 2;
}struct Func
{double operator()(double x){return x / 3;}
};int main()
{// 函数名cout << UseF(func, 11.11) << endl;// 函数对象cout << UseF(Func(), 11.11) << endl;// lambda表达式cout << UseF([](double x) {return x / 4; }, 11.11) << endl;return 0;
}// 由运行结果可见,UseF函数模板实例化了三份。
// 每出现一种新的函数定义,通过UseF调用就会使UseF实例化一个新的对象,这会导致效率低下。
// 包装器的出现就是为了解决这个问题。
#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
#include <functional>
using namespace std;// 通过包装器提高模板函数的效率
template<class F, class T>
T UseF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}double func(double x)
{return x / 2;
}struct Func
{double operator()(double x){return x / 3;}
};class Plus
{
public:static int plusi(int a){return a + 1;}double plusd(double a){return a + 1;}
};int main()
{// 函数名std::function<double(double)> f1 = func;cout << UseF(f1, 11.11) << endl;// 函数对象std::function<double(double)> f2 = Func();cout << UseF(f2, 11.11) << endl;// lambda表达式std::function<double(double)> f3 = [](double x) {return x / 4; };cout << UseF(f3, 11.11) << endl;// 类的成员函数std::function<double(Plus, double)> f4 = &Plus::plusd;cout << f4(Plus(), 11.11) << endl;return 0;
}

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

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式:auto newCallable = bind(callable,arg_list);

其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。

#define _CRT_SECURE_NO_WARNINGS 1// 使用举例
#include <iostream>
#include <functional>int Plus(int a, int b)
{return a + b;
}class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定std::function<int(int, int)> func1 = std::bind(Plus, std::placeholders::_1,std::placeholders::_2);//auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);//func2的类型为 function<void(int, int, int)> 与func1类型一样//表示绑定函数 plus 的第一,二为: 1, 2auto func2 = std::bind(Plus, 1, 2);std::cout << func1(1, 2) << std::endl;std::cout << func2() << std::endl;Sub s;// 绑定成员函数std::function<int(int, int)> func3 = std::bind(&Sub::sub, s,std::placeholders::_1, std::placeholders::_2);// 参数调换顺序std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,std::placeholders::_2, std::placeholders::_1);std::cout << func3(1, 2) << std::endl;std::cout << func4(1, 2) << std::endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/668609.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROS从入门到精通4-1:Docker安装与常用命令总结

目录 0 专栏介绍1 Docker与机器人应用2 Docker安装步骤3 Docker常用命令3.1 创建与启动容器3.2 暂停与删除容器3.3 容器文件拷贝3.4 构建镜像与上下文 0 专栏介绍 本专栏旨在通过对ROS的系统学习&#xff0c;掌握ROS底层基本分布式原理&#xff0c;并具有机器人建模和应用ROS进…

【文件增量备份系统】前端项目构建

文章目录 创建项目安装项目依赖引入element plus组件下载组件在main.js中使用组件测试 整合路由router下载组件创建路由管理器index.js使用路由App.vue上面使用 <router-view />测试 整合axios下载组件工具类axiosRequest.js工具类使用 创建项目 damwangrunqindeMBP dev…

车载测试中:如何处理 bug

一&#xff1a;Jira 提交 bug 包含那些内容 二&#xff1a;如何处理现上 bug 三&#xff1a;车载相关的 bug 如何定位 四&#xff1a;遇到 bug &#xff0c;复现不出来怎么办 五&#xff1a;bug 的处理流程 一&#xff1a;Jira 提交 bug 包含那些内容二&#xff1a;如何处理现上…

Java设计模式-模板方法模式(14)

行为型模式 行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对…

uniapp android和微信小程序如何实现PDF在线预览

Hello大家好&#xff01;我是咕噜铁蛋&#xff0c;UniApp在开发移动应用时提供了跨平台的解决方案&#xff0c;能够同时支持Android和iOS系统&#xff0c;而微信小程序则是一种轻量级的应用形式&#xff0c;可以在微信内直接运行。本文将探讨如何利用UniApp和微信小程序实现PDF…

k8s学习-Kubernetes的网络

Kubernetes作为编排引擎管理着分布在不同节点上的容器和Pod。Pod、Service、外部组件之间需要⼀种可靠的方找到彼此并进行通信&#xff0c;Kubernetes网络则负责提供这个保障。 1.1 Kubernetes网络模型 Container-to-Container的网络 当Pod被调度到某个节点&#xff0c;Pod中…

java数组学习

目录 1.数组概念 2.数组的定义 3.数组的静态初始化 4.地址值 5.数组元素访问 6.索引 7.数组的遍历 8.数组的动态初始化 9.数组两种初始化方式的区别 10.数组常见问题 1.数组概念 数组是一种容器&#xff0c;可以同来存储同种数据类型的多个值。但是数组容器在存储数据…

Redis核心技术与实战【学习笔记】 - 24.Redis 脑裂

简述 所谓脑裂&#xff0c;就是指在主从集群中&#xff0c;同时有两个主节点&#xff0c;它们都能接收写请求。而脑裂最直接的影响就是客户端不知道该往哪个主节点写入数据&#xff0c;结果就是不同的客户端会往不同的主机诶点上写入数据。而且&#xff0c;严重的话&#xff0…

从零开始手写mmo游戏从框架到爆炸(三)— 服务启动接口与网络事件监听器

上一章我们完成了netty服务启动的相关抽象&#xff08;https://blog.csdn.net/money9sun/article/details/136025471&#xff09;&#xff0c;这一章我们再新增一个全局的服务启动类&#xff0c;方便后续扩展。 服务启动 新增的两个类如下&#xff1a; 定义一个接口IServer …

Elasticsearch:使用 Inference API 进行语义搜索

在我之前的文章 “Elastic Search 8.12&#xff1a;让 Lucene 更快&#xff0c;让开发人员更快”&#xff0c;我有提到 Inference API。这些功能的核心部分始终是灵活的第三方模型管理&#xff0c;使客户能够利用当今市场上下载最多的向量数据库及其选择的转换器模型。在今天的…

npm出现 Error: EISDIR: illegal operation on a directory, read

npm出现 Error: EISDIR: illegal operation on a directory, read 一、问题二、解决 一、问题 可能是由于运行了npm config set cafile ""之类的方法,造成了cafile为空 二、解决 文件位于C:\Users\用户名\ 下 找到c盘下的Users下的用户目录&#xff0c;进入找到.n…

框架学习Maven

声明&#xff1a;本文来源于黑马程序员PDF讲义 做为一名Java开发工程师&#xff0c;后端 Web开发技术是我们学习的重点&#xff0c;后端Web开发技术的学习&#xff0c;我们会先学习Java项目的构建工具&#xff1a;Maven 初识Maven Maven是Apache旗下的一个开源项目&#xff…

Verilog实现2进制码与BCD码的互相转换

1、什么是BCD码&#xff1f; BCD码是一种2进制的数字编码形式&#xff0c;用4位2进制数来表示1位10进制中的0~9这10个数。这种编码技术&#xff0c;最常用于会计系统的设计里&#xff0c;因为会计制度经常需要对很长的数字做准确的计算。相对于一般的浮点式记数法&#xff0c;…

14.1 Ajax与JSON应用(❤❤)

14.1 Ajax与JSON应用 1. Ajax1.1 简介1.2 Ajax使用流程1. 前端创建XMLHttpRequest对象2. 发送Ajax请求3. 处理服务器响应4. 代码2. JSON2.1 简介2.2 JS解析JSON3. Ajax与JSON开发3.1 后端:用Jackson实现JSON序列化输出3.2 前端Ajax处理JSON3.3 Ajax工具

总结反思在部署上线短链接项目过程中所踩到的坑

总结反思在部署上线短链接项目过程中所踩到的坑 不容易&#xff0c;自己从零到一手敲的短链接项目&#xff0c;中间遇到了不少的曲折&#xff0c;终于部署上线了。 项目的上线地址&#xff1a;短链接系统 由于没有 2 核 4 g 服务器&#xff0c;就使用了两台 2 核 2 g 丐版服务…

2019年江苏省职教高考计算机技能考试——一道程序改错题的分析

题目&#xff1a;函数将str字符串中的5个数字字符串转换为整数&#xff0c;并保存在二维数组m的最后一行&#xff0c;各元素为3、-4、16、18、6。并经函数move处理后&#xff0c;运行结果如下&#xff1a; 18 6 3 -4 16 16 18 6 3 -4 -4 16 …

香港倾斜模型3DTiles数据漫游

谷歌地球全香港地区倾斜摄影数据&#xff0c;通过工具转换成3DTiles格式&#xff0c;将这份数据完美加载到三维数字地球Cesium上进行完美呈现&#xff0c;打造香港地区三维倾斜数据覆盖&#xff0c;完美呈现香港城市壮美以及维多利亚港繁荣景象。再由12.5米高分辨率地形数据&am…

【开源】JAVA+Vue+SpringBoot实现二手车交易系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 二手车档案管理模块2.3 车辆预约管理模块2.4 车辆预定管理模块2.5 车辆留言板管理模块2.6 车辆资讯管理模块 三、系统设计3.1 E-R图设计3.2 可行性分析3.2.1 技术可行性分析3.2.2 操作可行性3.2.3 经济…

02.05

1.单链表 main #include "1list_head.h" int main(int argc, const char *argv[]) { //创建链表之前链表为空Linklist headNULL;int n;datatype element;printf("please enter n:");scanf("%d",&n);for(int i0;i<n;i){printf("ple…

IDEA新建文件夹后右击不能创建class类排错方法

目录 1 查看自身文件名是否为关键词 2 查看是否被“蓝色文件夹”给包含了 3 检查设置那边的class模板 4 报错解决 1 查看自身文件名是否为关键词 如下使用了 Java中的关键词"class"所以才无法创建包 ---------------------------------------------------------…