【C++】C++11新增特性

news2024/12/24 2:36:42

目录

C++11简介:

1、统一的列表初始化:

std::initializer_list

2、自动类型推导:

auto:

decltype:

3、final 和 override

final:

override:

4、默认成员函数控制:

显示缺省函数:

删除默认函数:

5、左值和右值:

概念补充:

代码示例:

左值引用与右值引用:

两者的联系和区别:

右值引用的意义:

6、完美转发:

7、lambda表达式:

8、可变参数列表:

9、包装器:

function包装器:

bind(绑定):


C++11简介:

C++11,也被称为C++0x,是C++编程语言的一个重要更新版本,它于2011年正式被ISO标准委员会批准。相比于C++98,C++11带来了大量的新特性和改进,其中包含了约140个新特性,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率。本篇主要介绍一些使用的多且实用的一些语法:

1、统一的列表初始化:

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

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自 定义的类型,使用初始化列表时,可添加等号(=),也可不添加。

struct Student
{
	int _x;
	int _y;
};
int main()
{
	// 两种写法一样的
	int x1 = 1;
	int x2{ 2 };

	// 可以省去 = 
	int array1[]{ 1, 2, 3, 4, 5 };
	int array2[5]{ 0 };

	Student s{ 1, 2 };

	return 0;
}

创建对象时也可以使用列表初始化方式调用构造函数初始化

class A
{
public:

    // 如果不想让隐式类型转换发生可以加关键字: explicit
    // explicit A(int x, int y)

    A(int x, int y)
        :_x(x)
        , _y(y)
    {}


    // 单参数
    A(int x)
        :_x(x)
        , _y(x)
    {}

private:
    int _x;
    int _y;
};


int main()
{

    // 多参数的隐式类型转换
    // 本质是构造一个A对象,然后用这个对象去拷贝构造(编译器会进行优化, 就变成了直接构造)
    A a1 = { 1,2 };

    // 例如: A& a3 = {2, 2}; 这样子的就不行,但转换成下面的就可以
    const A& a3 = { 2,2 }; // 因为右边会产生临时变量,但临时变量具有常性

    // 当然也可以不加 = ,如 A a1{ 1,2 }; 但是不太推荐这么写 

    return 0;
}

std::initializer_list

initializer_list 是 C++11 引入的一个特性,用于表示某种类型对象的数组。它允许使用花括号 {} 包围的初始值列表来初始化对象或函数参数。initializer_list 通常与构造函数结合使用,以提供一种灵活的方式来初始化集合(如数组、向量等)或执行基于多个值的初始化。

(本质就是一个常量数组)

是个容器,但是没有新开空间,里面有两个指针,一个指向第一个元素(常量数组的开始),一个指向最后一个元素的后一个位置(常量数组的结束),所以是8/16字节,因为是两个指针(first, last)

2、自动类型推导:

auto:

auto 关键字用于自动类型推导。编译器会根据初始化表达式自动推断出变量的类型。使用 auto 可以让代码更加简洁,例如STL容器迭代器、函数返回类型等。

缺陷:

        auto虽然用起来非常方便,但也不能滥用哦,例如在层层嵌套的函数当中,用起来是爽了,但是代码的可读性会大大下降,这时维护起来也会特别麻烦,一个变量可能得剥好几层才知道类型,还有在涉及到函数重载以及模板元编程时,容易引发错误和意外的行为。

decltype:

decltype 关键字用于查询表达式的类型。与 auto 不同,decltype 在编译时解析表达式并得到其类型,但不实际计算表达式的值。这意味着可以使用 decltype 来获得几乎任何表达式的类型,包括那些没有定义(或不可计算)的表达式。

缺陷:

        decltype的语法相对复杂一点,且在某些情况下,decltype导出的类型可能非常冗长,特别是当表达式涉及到模板类型或复杂函数时。如果在函数声明中使用decltype时,如果函数的返回类型依赖于模板参数或函数参数的类型,那么可能需要使用尾置返回类型(trailing return type)语法,这可能会使函数声明的复杂性增加。

3、final 和 override

final:

final修饰类的时候,表示这个类无法被继承、修饰虚函数时,表示这个虚函数不能被重写。

修饰类:

修饰虚函数:

override:

override 关键字用于检查派生类虚函数是否重写了基类的某个虚函数,如果没有重写编译报错。

4、默认成员函数控制:

显示缺省函数:

当你不为类定义任何构造函数时,编译器会为你生成一个默认构造函数。如果你显式地定义了其他构造函数(例如拷贝构造、移动构造还是其他自定义构造函数),但没有定义默认构造函数,编译器不会自动生成默认构造函数。如果还想编译器生成默认构造函数的话,可以在类定义中显式地使用 default 关键字来请求编译器生成默认构造函数。

删除默认函数:

如果想要一个类禁止被拷贝,在C++98当中的做法是将这个类的拷贝构造以及赋值重载直接声明为私有(private),在C++11中则更为简单一些,只需在该函数声明加上delete关键字即可

5、左值和右值:

概念补充:

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用的返回)等等,右值可以出现在赋值符号的右边,但是不能出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。

简记:左值能出现在右边,右值不能出现在左边。

代码示例:

左值引用与右值引用:

可以先简短的记着:左值引用就是给左值取别名,右值引用就是给右值取别名!

先来谈谈简单的:左值引用:

int main()
{
	int a = 10;
	int& b = a; // b就是a的别名,其实就是a

	// 属于同一块地址空间:
	cout << &a << endl;
	cout << &b << endl;

	return 0;
}

再来谈谈右值引用:

int main()
{
	int x = 1, y = 2;
	int&& z = (x + y);

	string&& s1 = string("111111");
	
	string&& s2 = to_string(1234);

	int&& a = 10;

	return 0;
}

两者的联系和区别:

右值引用的本身是左值,因为只有这样才能实现移动构造和移动赋值,以此来实现资源转移,因为普通左值是没法引用右值的,必须加const,但是加了又没法实现资源的转移。

所以右值引用是有地址的:

	string&& s1 = string("111111");
	cout << &s1 << endl;

如果右值引用的属性是右值,那么移动构造和移动赋值,要转移资源的语法逻辑是矛盾的,右值是不能被改变的,可以理解为右值带有const属性。

诶?那再回头来看看,既然右值引用是给右值取别名,那么右值引用有地址,那么是不是右值本身是有地址的???

其实还真是,像刚才的那种右值的底层是有地址的,但是只有编译器知道,为了语法的逻辑自洽,他不会让你取得到这个地址,右值引用s1其实就是变相获取到这个地址,所以能不能取到地址不是关键,关键是资源的转移(匿名对象和临时对象都带有const属性)

一般情况下左值引用只能引用左值,不能引用右值。但是加了const的左值引用既能引用左值也能引用右值。

一般情况下右值引用只能引用右值,不能引用左值。但是右值引用可以引用move以后的左值。

move函数:将一个左值对象转化为右值引用,从而允许使用移动语义来优化资源的管理和程序的性能。

右值引用的意义:

右值引用的意义主要体现在两个方面:移动语义、完美转发。

移动语义:

1、资源所有权转移:在C++中,类的右值通常是一个临时对象,如果在表达式结束时没有被绑定到引用,就会被废弃。通过右值引用,可以在对象被废弃之前移走其资源,实现资源的再利用,再需要时,就可以避免无意义的拷贝复制操作,提高效率。

2、减少开销:被移走资源的右值在废弃时已经成为空壳,其析构的开销会大大降低。这有助于提升程序的性能,特别是在处理大型对象或资源密集型操作时。

完美转发:

这里简单讲一下,下一标题再详细叙述。

完美转发的实现依赖于右值引用。右值引用(T&&)用于绑定到即将被销毁的对象上,从而允许资源的移动而非拷贝。

在模板函数中,通过使用 T&& 作为参数类型,能够接受左值和右值作为参数。

6、完美转发:

完美转发(Perfect Forwarding)是C++11中引入的一种编程技巧,其目的是在编写泛型函数时能够保留参数的类型和值类别(左值或右值),从而实现更为高效且准确地传递参数。

完美转发允许函数模板将其接收到的参数“完美”地转发给内部调用的其他函数,这里的“完美”指的是不仅能准确地转发参数的值,还能保证被转发参数的左、右值属性不变。

省流:

完美转发就是在函数模板中,将参数转发给其他函数时,可以锁定参数的左右值属性和值类别。

因为右值引用的对象在作为实参传递时,属性会退化为左值,会直接匹配左值引用,使用完美转发可以保持它的右值属性。

这里来认识两个东西:forward函数和万能引用(T &&)

forward函数:是一个模板函数,用于实现完美转发

万能引用(T &&):既能接收左值也能接收右值。

直接上一段代码来直观感受一下:

7、lambda表达式:

lambda表达式从C++11标准开始引入的一种定义匿名函数对象的简洁方式。它可以捕获它所在作用域的变量,并可以在需要函数对象的任何地方使用,包括作为算法的一部分或作为回调函数。

基本语法:

[capture](parameters) mutable -> return_type {  
    // 函数体  
}
  • capture捕获列表,指定哪些外部变量在lambda函数体内是可用的。捕获列表可以是按值捕获(例如[x])或按引用捕获(例如[&x]),也可以混合使用(例如[&, x = this->x]),其中&表示按引用捕获所有外部变量,除了显式按值捕获的。=表示按值捕获所有外部变量,除了显式按引用捕获的。
  • parameters参数列表,与普通函数相同。如果lambda不接受任何参数,则可以省略括号。
  • mutable这是一个可选的说明符,表示lambda函数体内的代码可以修改按值捕获的变量。默认情况下,这些变量是只读的。
  • return_type返回类型,也是可选的。如果lambda体只有一个返回语句,且编译器能够从该语句推断出返回类型,则可以省略。如果lambda没有返回语句,则其返回类型为void

也可以就简单的记为:[](){}

其实本质就是仿函数,编译器在编译的时候就会转成仿函数,原理类似范围for

举个小例子:我给这个lambda表达式传两个值,让它返回这两个值的和给我:

补充:

int main()
{
    int a = 1, b = 2;
    // 捕捉列表

    auto swap1 = [a, b]() mutable // 还是传值捕捉
    {
        int tmp = a;
        a = b;
        b = tmp;
    };
    swap1();
    // 如果不加mutable的话a, b捕捉到的默认是const的,无法对其做出修改操作,而且·此ab非局部的那俩ab
    // 也就是就算带mutable修改内部也并不会改变外部
    printf("%d %d\n", a, b);


    // 引用捕捉可修改:
    auto swap2 = [&a, &b]() {
        int tmp = a;
        a = b;
        b = tmp;
    };
    swap2();
    printf("%d %d", a, b);

    // 捕捉方式还有 =, &
    // = 为传值捕捉所有父作用域中的变量,包括this
    // & 为引用捕捉所有父作用域中的变量,包括this

    // 还可以混合捕捉: 表示为&捕捉全局,唯独b为传值捕捉(也可以反着来)
    auto func = [&, b]() {};

    return 0;
}

8、可变参数列表:

省流:可变参数列表一种特殊的函数参数机制,允许函数接收数量不确定的参数

先来段简单的代码看看使用方法:

既然可变参数列表可以接收不确定数量的参数,那么仔细想想,好像printf函数也是如此,那么是不是就可以简单的模拟实现一下printf函数呢?

总结:可变参数列表提供了极高的灵活性和类型安全性,且支持泛型编程,但复杂性较高,可能会导致编译时间的增加和额外的性能开销。

9、包装器:

function包装器:

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。于头文件#include <functional>

其实包装器的本质就是函数指针,可调用对象有:函数、函数对象(仿函数)、lambda表达式、绑定表达式等。

先来看一个例子:来看看下面这段代码中的函数模板会被实例化几次?

template<class F, class T>
T Test(F f, T t)
{
	static int x = 0;
	cout << "x: " << ++x << endl;
	cout << "x: " << &x << endl;

	return f(t);
}

double f(double i)
{
	return i / 2;
}

// 仿函数
struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};

int main()
{
	// 函数:
	cout << Test(f, 11.11) << endl;

	// 函数对象(仿函数):
	cout << Test(Functor(), 11.11) << endl;

	// lambda表达式:
	cout << Test([](double d) {return d / 4; }, 11.11) << endl;

	return 0;
}

通过结果我们可以看出来,函数模板实例化了三次(静态变量x的地址都不一样)

对于同一个模板类或模板函数,使用多种不同的类型参数去实例化它时。每次使用不同的类型参数实例化模板,编译器都会生成一份独立的模板代码。这可能导致编译时间增加、二进制文件大小膨胀,以及潜在的运行时性能问题(如指令缓存未命中增加)。

但是我只想让这个函数模板实例化一次怎么办呢?

这时就可以掏出function包装器来解决:

function用法解析:

代码结果:

这样实例化出来的就是同一份模板了(x依次递增,且为同一地址),这样通过function包装器就可以减少不必要的模板实例化,因为模板实例化多了容易导致代码膨胀,降低效率。

最重要的是:好用(滑稽)

bind(绑定):

省流:调整可调用对象的参数个数或者顺序:

完整概念:

std::bind 用于生成一个新的可调用实体(function object),这个新的可调用实体可以把它的某些(或全部)参数绑定到给定的值上。这样,当你调用这个新的可调用实体时,就不需要再为那些已经被绑定的参数提供值了。(头文件:#include <functional>

基本语法:

auto newCallable = bind(callable, arg_list, placeholders::_1, placeholders::_2, ...);
  • callable:你想要调用的函数、函数对象、成员函数指针或成员对象指针。
  • arg_list:你希望预先绑定到 callable 的参数列表。这些参数在后续调用 newCallable 时将不再需要。
  • placeholders::_1, placeholders::_2, ...:占位符,用于表示 newCallable 被调用时,需要接收的参数位置。允许保留一些参数位置以供将来调用时提供。

上个简单的代码看看:

再来看一组:也可以绑定类中的函数:

注意:

        绑定成员函数时,需要传递成员函数的地址(使用&运算符),并且还需要传递一个指向对象实例的指针(对于非静态成员函数)或对象的引用(但通常期望指针,因为bind需要能够存储这个引用所指向的对象的状态,而局部引用在函数返回后可能不再有效)。

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

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

相关文章

网络安全简介(入门篇)

目录 前言 一、什么是网络安全&#xff1f; 二、网络安全的重要性 1、保护数据安全和隐私 2、防止服务中断和数据丢失 3、防止经济损失和法律责任 4、维护公共安全和国家安全 5、提升技术发展和创新 三、网络安全等级保护 1、第一级&#xff08;自主保护级&#xff0…

Java基础-Windows开发环境下Java8和OpenJDK17的自由切换

一、Java语言介绍 Java是一种广泛使用的编程语言和计算平台&#xff0c;具有平台无关性、稳定性和强大的生态系统。随着时间的推移&#xff0c;Java不断演进&#xff0c;推出了多个版本&#xff0c;每个版本都带来了新的功能和性能改进。 1. Java主流版本 Java的主流版本包括…

【排序篇】快速排序的非递归实现与归并排序的实现

&#x1f308;个人主页&#xff1a;Yui_ &#x1f308;Linux专栏&#xff1a;Linux &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;数据结构专栏&#xff1a;数据结构 文章目录 1 快速排序非递归2. 归并排序3.排序算法复杂度及稳定性分析 1 快速排序非递归 利…

Leetcode面试经典150题-14.最长公共前缀

解法都在代码里&#xff0c;不懂就留言或者私信 这个题比较简单&#xff0c;基本上是笔试的第一第二题 class Solution {/**最长公共前缀这种问题个人感觉最重要的是剪枝我理解可以先按照字符串的长度排个序&#xff0c;然后把第一个字符串的长度作为暂时的最长公共前缀的长度…

分享一个基于python爬虫的“今日头条”新闻数据分析可视化系统(源码、调试、LW、开题、PPT)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人 八年开发经验&#xff0c;擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等&#xff0c;大家有这一块的问题可以一起交流&…

【装修】出租屋改造计划,米家IoT全屋智能家居方案 投影仪与家庭ktv游戏影院方案

【装修】出租屋改造计划&#xff0c;米家IoT全屋智能家居方案 & 投影仪与家庭ktv游戏影院方案 文章目录 1、目标效果 & 预算2、网络&#xff1a;路由 & 中枢网关方案3、卧室&#xff1a;灯光系列 & 环境系列4、米家&#xff1a;其他房间&#xff08;暂不装&…

C++ 内存布局 - Part2: 从内存布局角度看类型转换 static_cast, dynamic_cast, reinterpret_cast

0. 总论 开门见山&#xff0c;先把结论写在这里&#xff1a; 1&#xff09;static_cast 在做基类指针和派生类指针之间的转换时&#xff0c;会根据编译时的静态偏移操作指针&#xff0c;但是没有运行期的类型安全检查&#xff0c;程序员需要自己确保类型的正确性&#xff0c;…

ollama轻松部署本地GraphRAG(避雷篇)

本篇文章主要介绍如何使用ollama本地部署微软的Graph RAG&#xff0c;&#xff0c;Graph RAG成为RAG一种新范式&#xff0c;对于全局总结性问题表现突出&#xff0c;当开始的技术路线是Qwen2vllmfastchat本地部署Grapg RAG&#xff0c;但是embedding 的openai服务怎么都跑不起来…

完整搭建windows下mysql8.0源码编译调试环境!

背景&#xff1a; 前段时间一直在看mysql相关的博客&#xff0c;所以对源码起了浓厚的兴趣&#xff0c;所以尝试通过vmware和vscode在windosw环境中搭建一套编译调试的环境~ 看了一下网上的搭建教程基本杂乱无章&#xff0c;想要从零跟着搭建出一个完善的调试环境也不是易事&…

redis I/O复用机制

I/O复用模型 传统阻塞I/O模型 串行化处理&#xff0c;就是要等&#xff0c;假如进行到accept操作&#xff0c;cpu需要等待客户端发送的数据到tcp接收缓冲区才能进行read操作&#xff0c;而在此期间cpu不能执行任何操作。 I/O复用 用一个进程监听大量连接&#xff0c;当某个连…

国产大模型领域跳槽:收入潜力解析

夏尽秋来&#xff0c;2024年国产大模型看似喧闹已止&#xff0c;进入稳定竞争期。 作为一种新的IT解决方案&#xff0c;国产大模型一出生便伴随着激烈竞争。 外有GPT4&#xff0c;内有多家公司角逐“中国版ChatGPT”。 据我所知&#xff0c;就国内某家头部大模型创业公司的收…

0817(持久层框架:JDBC,MyBatis)

三层架构&#xff08;表现层&#xff0c;业务层&#xff0c;持久层&#xff09; java中框架的概述&#xff08;表现层、业务层、持久层的关系&#xff09;_控制层业务层持久层的关系-CSDN博客 框架&#xff1a;框架一般处在低层应用平台&#xff08;如J2EE&#xff09;和高层…

利用keepalived达成服务高可用

官方网站Keepalived for Linux 1.keepalived简介 vrrp 协议的软件实现&#xff0c;原生设计目的为了 高可用 ipvs 服务 功能&#xff1a; 基于 vrrp 协议完成地址流动 为 vip 地址所在的节点生成 ipvs 规则 ( 在配置文件中预先定义 ) 为 ipvs 集群的各 RS 做健康状态检测 …

【Linux网络】NAT技术

欢迎来到 破晓的历程的 博客 ⛺️不负时光&#xff0c;不负己✈️ 引言 随着互联网的飞速发展&#xff0c;IP地址资源日益紧张&#xff0c;这促使了NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;技术的诞生与发展。NAT技术不仅解决了IPv4…

webshell免杀--免杀入门

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文主要整理webshell免杀的一些基础思路 入门级&#xff0c;不是很深入&#xff0c;主要是整理相关概念 免杀对象 1.各类杀毒软件 类似360&#xff0c;火绒等&#xff0c;查杀己方webshell的软件。 2.各类流量…

计算机网络系统速成

Http与Https Http与Https是两种重要的网络通信协议&#xff0c;它们在Web通信中扮演着关键角色。以下是对Http与Https的详细解析&#xff1a; 一、Http&#xff08;超文本传输协议&#xff09; 1. 定义与功能 定义&#xff1a;Http&#xff08;HyperText Transfer Protocol…

打卡学习Python爬虫第三天|电影天堂案例

一、明确需求 目标&#xff1a;爬取最新更新的电影的豆瓣链接 观察网页和页面源代码&#xff0c;每部电影都有一个超链接去到子页面&#xff0c;我们需要的内容在子页面&#xff0c;如果我们一个一个子页面的去爬取会比较麻烦&#xff0c;可以尝试先通过首页爬取子页面的超链…

指针初阶(指针类型转换的使用、指针数组)

一.指针基础 0.指针的大小 指针指向的是一块地址&#xff0c;所以指针存储的是地址&#xff0c;例如在32位系统中。定义了一个int32_t类型的变量。使用int32_t *定义一个指针&#xff0c;使其指向该变量。 设该变量存储在地址为00000000000000000000000000000001&#xff08;3…

远程调用-OpenFeign(一)

目录 1.RestTemplate存在问题 2.OpenFeign介绍 一、主要特点 二、应用场景 3.OpenFeign快速上手 3.1引入依赖 3.2添加注解 3.3编写OpenFeign的客户端 3.4远程调用 ​编辑3.5测试 4.OpenFeign参数传递 4.1传递单个参数 4.2传递多个参数 4.3传递对象 4.4传递JSO…

编程修炼之Hibernate--- springboot启动初始化ddl过程

文章目录 跟踪Springboot整合hibernate的启动代码&#xff1a; 开始初始化 entityManagerFactory 创建方言 dialect 继续排查