C++ | C++11新特性(上)

目录

前言

一、列表初始化

二、声明

1、auto

2、decltype

3、nullptr

三、STL容器的变化

四、右值引用与移动语义

1、左值与左值引用

2、右值与右值引用

3、右值引用与左值引用的比较

4、右值引用的场景及意义

(1)做参数 

(2)返回值

5、完美转发 

(1)万能引用 

(2)完美转发

五、类的新功能

1、默认成员的改变

2、default与delete关键字

3、final与override


前言

        本章主要讲解一些关于C++11常用语法;不会将每个语法都介绍一边,将主要语法进行讲解;如果想要了解全部有关C++11语法可以访问下方链接网站;

C++11

一、列表初始化

        没错,就是列表初始化,并不是初始化列表,一定要搞清楚了。这里说的是列表初始化,并不是构造函数中的初始化列表;其实列表初始化在C语言中也有体现,如下代码;

// 列表初始化
struct point
{int _x;int _y;
};void test1()
{// C++98 C语言版的列表初始化int arr1[] = { 1,2,3,4,5 };int arr2[5] = { 0 };struct point p = { 1,2 };
}

        在C语言中,我们可以通过花括号的方式对数组和结构体进行初始化;C++11中,我们同样提供了这种初始化方式;万物皆可花括号初始化;其中等号一般可省略;

	// C++11 万物皆可花括号(= 可省略)int x1 = 3;int x2{ 3 };int* pa = new int[2]{ 3 };

        其实,不仅是我们内置成员可以这么初始化,连我们自定义类型也可以这样初始化,如下述代码所示;

	string str1 = { "hhhh" };string str2{ "hhhh" };vector<int> v1 = { 1,2,3,4,5,6 };vector<int> v2 { 1,2,3,4,5,6 };list<int> l1 = { 1,2,3,4,5,6 };list<int> l2 { 1,2,3,4,5,6 };Date d1 = { 2022, 8, 5 };Date d2 { 2022, 8, 5 };

        这是因为我们的C++11中,为我们提供了一种新的类型,这个类型叫initializer_list;而我们的STL容器都提供了一个这个版本的构造;我们可以通过下述代码证明initializer_list的存在;

void test2()
{// initializer_list  头文件<initializer_list>auto li1 = { 1,2,3,4,5,6 };initializer_list<int> li2 = { 1,2,3,4,5,6 };cout << typeid(li1).name() << endl;cout << typeid(li2).name() << endl;}

        一个是通过auto自动识别类型,一个是显示声明;可输出的结果都相同;

        自C++11后,许多容器都提供了这种初始化的方式; 

二、声明

1、auto

        auto其实在C++98时就存在了,但由于在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。而在C++11中,废除了以前的用法,实现了自动类型推断,在很多场景下都变得好用了许多;

void test4()
{map<int, int> m1;map<int, int>::iterator it1 = m1.begin();auto it2 = m1.begin();
}

2、decltype

        auto用于类型自动识别,而我们的则是可以推导我们的表达式类型,并用该类型进行初始化;具体用法如下;

void test3()
{// decltypeint a = 1;int b = 2;decltype(a + b) c = 10;cout << typeid(c).name() << endl;// 应用vector<decltype(a + b)> v1;cout << typeid(v1).name() << endl;
}

3、nullptr

        没错,我们平常使用的nullptr也是C++11后推出的,那为什么不用NULL呢?实际上NULL的定义存在BUG;具体细节可以移步下方链接最后一个知识点;

C++初阶

三、STL容器的变化

        在我们C++11后,我们的容器也有了很大的变化;如下图所示;

        其中,array对标的是我们C语言的数组,可实际上,我们有非常好用的vector了,因此array使用并不怎么需要了;但C++11中unordered系类的容器有非常显著的效果,确实挺好用,比起map与set,虽然unordered系类并不排序,但是效率明显会比map与set高很多,其增删查改的时间复杂度都接近O(1);同时C++11的容器中提供了const迭代器版本cbegin。cend,但实际上用的也不多;

四、右值引用与移动语义

        C++11增加的右值相关语法进一步提高了我们代码的效率;是一个非常有用的知识点;下面我们首先了解什么叫左值,什么又叫右值;

1、左值与左值引用

        左值是一个表示数据的表达式;我们可以对他进行取地址以及赋值;左值既可以出现在=的左边,也可以出现在=的右边;左值引用就是左值的引用,左值的别名;

void test5()
{// a/b/p都是左值int a = 1;const int b = 4;int* p = new int(10);// ra/rb/rp都是左值引用int& ra = a;const int& rb = b;int*& rp = p;
}

2、右值与右值引用

        右值也是一个表示数据的表达式,但是不同的是右值通常是字面量、表达式返回值、函数返回值等;右值只能出现在=的右边,不能出现在=的左边;且右值无法取地址;这是二者最本质的区别;右值引用就是右值的引用,右值的别名;

void test6()
{int a = 1;int b = 4;// 以下均为右值a + b;123;func();// 以下均为右值引用int&& r1 = a + b;int&& r2 = 123;int&& r3 = func();
}

注意:关于上述,我们区分左值和右值的可通过是否可以取地址来进行判断,若可取地址,则必定是左值,若不可则是右值,这里还有一个小小的补充,右值引用在引用右值后,保存进了一个变量,该变量是左值,可以进行取地址;

void test7()
{int a = 3;int b = 6;// a + b是右值,ra是右值引用,ra变量是左值int&& ra1 = a + b;const int&& ra2 = a + b;cout << &ra1 << endl; // 可以取地址// cout << &(a + b) << endl; // err 右值不能取地址ra1 = 20; // 正确// ra2 = 20; // err const修饰的右值引用不可修改
}

3、右值引用与左值引用的比较

左值引用:

1、可以引用左值

2、const修饰后既可引用左值,也可引用右值

右值引用:

1、可以引用右值

2、可以引用move后的左值

void test8()
{int a = 2;int b = 5;// 左值引用可以引用左值// const佐治引用可以引用右值int& ra1 = a;//int& ra2 = (a + b); // err 不能引用右值const int& ra3 = (a + b); // const修饰的左值引用可以引用右值// 右值引用可以引用右值// 右值引用可以引用move后的左值int&& ra4 = (a + b);// int&& ra5 = a; // err 不可引用左值int&& ra6 = move(a); // 可引用move后的左值
}

4、右值引用的场景及意义

        关于右值引用的场景与意义,我们使用我们之前封装的简略版string进行展示; 

namespace MySpace
{class string{public:string(const char* str = ""):_size(strlen(str)), _capacity(_size){cout << "string(char* str)" << endl;_str = new char[_capacity + 1];strcpy(_str, str);}// s1.swap(s2)void swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(string s) -- 深拷贝" << endl;string tmp(s);swap(tmp);return *this;}string operator+(char ch){string tmp(*this);tmp.push_back(ch);return tmp;}~string(){delete[] _str;_str = nullptr;}void reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void push_back(char ch){if (_size >= _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = ch;++_size;_str[_size] = '\0';}//string operator+=(char ch)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity; // 不包含最后做标识的\0};
}
(1)做参数 

        我们给上述的string类添加一个移动构造版本; 形参是右值时才会调用这个版本;一般右值都是一些声明周期快要介绍的值;可能即将会被销毁;我们可以直接偷走右值的资源来减少深拷贝的次数;代码如下所示;

		// 移动构造(拷贝构造特殊版本)string(string&& s):_str(nullptr), _size(0), _capacity(0){cout << "string(string&& s)  -- 移动语义" << endl;swap(s);}

        我们再对上述情况进行分析;当我们传入一个右值时,我们调用移动构造,直接转移其资源,而不进行深拷贝;

(2)返回值

        我们的左值引用解决了当我们传回非局部对象时的拷贝问题;但是对于局部对象,我们还是需要进行深拷贝;下面以to_string函数作为举例;

        但是在我们C++11后,引入了右值引用的概念;我们同样使用这段代码; 

        同样,不仅有移动构造,还有移动赋值,原理是一样的,通过识别形参是左值还是右值选择不同的接口, 如果是左值就采用深拷贝的方式进行;如果是右值,则采用移动资源的方式来进行;同样库里的STL容器也更新了许多关于移动语义的接口;

5、完美转发 

有如下程序,猜测如下程序的输出结果是什么?

void Fun(int& x) 
{ cout << "左值引用" << endl; 
}
void Fun(const int& x) 
{ cout << "const 左值引用" << endl; 
}
void Fun(int&& x) 
{ cout << "右值引用" << endl; 
}
void Fun(const int&& x) 
{ cout << "const 右值引用" << endl; 
}
template<typename T>
void PerfectForward(T&& t)
{Fun(t);
}void test10()
{int a = 10;PerfectForward(a);PerfectForward(move(a));const int b = 20;PerfectForward(b);PerfectForward(move(b));
}

结果都是我们的左值引用,这个结果你猜到了吗? 

(1)万能引用 

        在上述代码中,我们将模板中的&&成为万能引用;也叫折叠引用; 

 

(2)完美转发

        完美转发的情况就诞生在上述情况中,当我们用右值引用接收右值时,我们的右值引用对象接收右值,并找个位置储存起来;此时我们的引用对象实际上是左值;当我们想往下一层调用传达右值就无法做到了;如上述我们PerfectForward接收后,用右值引用对象 t 保存了下来,此时 t 实际上已经是左值了,所以我们传给下一层时,传的时左值引用的接口;我们可以称这种现象为丢失了右值的属性;此时我们可以用我们的 forward 保持其原有的右值属性,这种方式我们称之为完美转发;如下述代码;

void Fun(int& x) 
{ cout << "左值引用" << endl; 
}
void Fun(const int& x) 
{ cout << "const 左值引用" << endl; 
}
void Fun(int&& x) 
{ cout << "右值引用" << endl; 
}
void Fun(const int&& x) 
{ cout << "const 右值引用" << endl; 
}
template<typename T>
void PerfectForward(T&& t)
{Fun(forward<T>(t));
}void test10()
{int a = 10;PerfectForward(a);PerfectForward(move(a));const int b = 20;PerfectForward(b);PerfectForward(move(b));
}

五、类的新功能

1、默认成员的改变

        之前类与对象中我们提过,类有六大默认成员,分别为默认构造、拷贝构造、赋值重载。析构函数、取地址重载以及const取地址重载;C++11后,又新增了两个默认的成员函数,分别为移动构造函数移动赋值重载函数

移动构造函数与移动赋值重载函数具有以下特性:

1、若自己未显式声明没有实现析构函数、拷贝构造、赋值重载中任意一个,则生成默认的

2、默认生成的移动构造/移动赋值对内置成员会逐字节拷贝,对于自定义成员则调用他们的移动构造/移动赋值,若该自定义成员没有移动构造/移动赋值,则调用他们的拷贝构造/赋值;

class Person
{
public:Person(int age = 18, MySpace::string name = "Jack"):_age(age),_name(name){}
private:int _age;MySpace::string _name;
};

        此时我们的Person类满足上述自动生成默认移动构造与移动赋值的条件;并且我们发现对于自定义成员sting也调用了其移动构造与移动赋值; 

2、default与delete关键字

        default关键字可以让编译器帮我们生成默认的成员函数函数,delete关键字可以不让编译器生成默认的成员函数,即使已经满足自动生成条件;如下所示;

class A
{
public:// 生成默认的构造A() = default;A(int a) :_a(a){}// 不允许生成拷贝构造A(const A& a) = delete;
private:int _a;
};void test12()
{A a1;A a2 = a1; // err 拷贝构造函数被禁止生成了}

3、final与override

        final作用有二,其一是修饰类,使得该类不允许被继承下去了;其二是修饰虚函数,使得该虚函数不能被继续重写了;

        override的作用则是修饰虚函数,检查子类虚函数是否重写;

class Base
{
public:virtual void func1() final // 该虚函数无法被重写{cout << "virtual void func1() final" << endl;}virtual void func2(){cout << "virtual void func2()" << endl;}
private:int _b;
};class Deriver : public Base
{
public:/*void func1(){cout << "void func1()" << endl;}*/void func2() override  // 检查是否重写父类虚函数{cout << "class Deriver : public Base" << endl;}
private:int _d;
};

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

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

相关文章

【暑期每日一练】 Epilogue

目录 选择题&#xff08;1&#xff09;解析&#xff1a; &#xff08;2&#xff09;解析&#xff1a; &#xff08;3&#xff09;解析&#xff1a; &#xff08;4&#xff09;解析&#xff1a; &#xff08;5&#xff09;解析&#xff1a; 编程题题一描述输入描述&#xff1a;输…

基于Vue+wangeditor实现富文本编辑

目录 前言分析实现具体解决的问题有具体代码实现如下效果图总结前言 一个网站需要富文本编辑器功能的原因有很多,以下是一些常见的原因: 方便用户编辑内容:富文本编辑器提供了类似于Office Word的编辑功能,使得那些不太懂HTML的用户也能够方便地编辑网站内容。提高用户体验…

力扣 -- 139. 单词拆分

一、题目 题目链接&#xff1a;139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; 二、解题步骤 下面是用动态规划的思想解决这道题的过程&#xff0c;相信各位小伙伴都能看懂并且掌握这道经典的动规题目滴。 三、参考代码 class Solution { public:bool wordBreak(str…

【Change】50 Matplotlib Visualizations, Python实现,源码可复现

详情请参考博客: Top 50 matplotlib Visualizations 因编译更新问题&#xff0c;本文将稍作更改&#xff0c;以便能够顺利运行。 1 Time Series Plot 时间串行图用于可视化给定指标如何随时间变化。在这里&#xff0c;您可以看到1949年至1969年间航空客运量的变化。查看此免费…

总结七大排序!

排序总览 外部排序&#xff1a;依赖硬盘&#xff08;外部存储器&#xff09;进行的排序。对于数据集合的要求特别高&#xff0c;只能在特定场合下使用&#xff08;比如一个省的高考成绩排序&#xff09;。包括桶排序&#xff0c;基数排序&#xff0c;计数排序&#xff0c;都是o…

Kafka入门,保姆级教学

文章目录 Kafka概念消息中间件对比消息中间件对比-选择建议Kafka常用名词介绍Kafka入门1. Kafka安装配置2.Kafka生产者与消费者关系3.Kafka依赖4.生产者发消息5.消费者接受消息6.Kafka高可用性设计6.1集群Kafka备份机制(Reolication) 7.kafka生产者详解7.1 发送类型7.2参数详解…

【数学建模学习(9):模拟退火算法】

模拟退火算法(Simulated Annealing, SA)的思想借 鉴于固体的退火原理&#xff0c;当固体的温度很高的时候&#xff0c;内能比 较大&#xff0c;固体的内部粒子处于快速无序运动&#xff0c;当温度慢慢降 低的过程中&#xff0c;固体的内能减小&#xff0c;粒子的慢慢趋于有序&a…

Stephen Wolfram:嵌入的概念

The Concept of Embeddings 嵌入的概念 Neural nets—at least as they’re currently set up—are fundamentally based on numbers. So if we’re going to to use them to work on something like text we’ll need a way to represent our text with numbers. And certain…

nginx环境部署

目录 一、yum安装 二、源码安装 三、测试结果 一、yum安装 1、先查找本地yum源上有没有nginx包 yum list | grep nginx 2、rpm安装 rpm -Uvh http://nginx.org/packages/centos/7/x86_64/RPMS/nginx-1.14.2-1.el7_4.ngx.x86_64.rpm 3、查看安装是否成功 rpm -pa | grep…

Go 语言面试题(一):基础语法

文章目录 Q1 和 : 的区别&#xff1f;Q2 指针的作用&#xff1f;Q3 Go 允许多个返回值吗&#xff1f;Q4 Go 有异常类型吗&#xff1f;Q5 什么是协程&#xff08;Goroutine&#xff09;Q6 如何高效地拼接字符串Q7 什么是 rune 类型Q8 如何判断 map 中是否包含某个 key &#xf…

如何解决电脑无声问题:排除故障的几种常见方法

大家好&#xff0c;今天我们来讨论一下处理电脑没有声音的故障。当你突然发现电脑静音无声时&#xff0c;需要逐步排除可能的问题&#xff0c;但总体而言&#xff0c;声音故障是相对容易解决的。接下来&#xff0c;我们将介绍一些排除电脑无声问题的方法。 第一步&#xff1a;…

HTML之表单标签

目录 表单标签 Form表单 定义&#xff1a; 基本语法结构&#xff1a; form属性&#xff1a; enctyoe属性 fieldeset标签 fieldeset属性 legend标签 label标签 优势 label属性 input标签 input属性 input标签中的type属性 text text输入框有以下配套属性 searc bu…

Java中实现图片和Base64的互相转化

文章目录 前言一、代码二、测试三、结果 前言 公司项目中用到了实名认证此&#xff0c;采用的第三方平台。后端中用到的单项功能为身份证信息人像对比功能&#xff0c;在写demo的过程中发现&#xff0c;它们所要求的图片信息为base64编码格式。 一、代码 package com.bajiao…

企业如何搭建矩阵内容,才能真正实现目的?

当下&#xff0c;新媒体矩阵营销已成为众多企业的营销选择之一&#xff0c;各企业可以通过新媒体矩阵实现扩大品牌声量、维持用户关系、提高销售业绩等不同的目的。 而不同目的的矩阵&#xff0c;它的内容运营模式会稍有差别&#xff0c;评价体系也会大不相同。 企业在运营某类…

python sqlalchemy 动态设置表名__tablename__,一个model对应多个table

我们在上一篇中说明了&#xff0c;如何在.net core的efcore中动态设置表名。 本文讲述如何在sqlalchemy中动态设置表名&#xff0c;使多个table可以对应到一个model 表如下 code example from sqlalchemy import create_engine,Column,BigInteger,String from sqlalchemy.ext…

2. Linux安装Git

yum安装 查看版本 版本太低&#xff0c;所以我们采用自己上传编译的方式进行 删除已安装的git yum remove git 下载最新安装包&#xff0c;并上传到服务器文件夹下 上传&#xff0c;解压 5.安装编译需要的依赖 yum install curl-devel expat-devel gettext-devel openssl-…

PPS Tester测量原理和实施方法

怿星科技发布了新品PPS Tester&#xff0c;这是一款基于1PPS方法的时间同步精度测试设备。PPS Tester由硬件模块ETS2110和上位机软件ePPSTester构成。本文将围绕此设备的应用场景&#xff0c;介绍相关概念和设备使用方法。 什么是时间同步&#xff1f; 时间同步就是采取某项技…

3、详解桶排序及排序内容总结

堆 满二叉树可以用一个数组中从0开始的连续一段来记录 i i i位置左孩子: 2 ∗ i + 1 2*i+1 2∗i+1,右孩子: 2 ∗ i + 2 2*i+2 2∗i+2,父: ( i − 1 ) / 2 (i-1)/2 (i−1)/2 大根堆 每一棵子树的根为最大值 小根堆 每一棵子树的根为最小值 建大根堆 不断地根据公…

7_分类算法—逻辑回归

文章目录 逻辑回归&#xff1a;1 Logistic回归&#xff08;二分类问题&#xff09;1.1 sigmoid函数1.2 Logistic回归及似然函数&#xff08;求解&#xff09;1.3 θ参数求解1.4 Logistic回归损失函数1.5 LogisticRegression总结 2 Softmax回归&#xff08;多分类问题&#xff0…

只会用插件可不行,这些前端动画技术同样值得收藏-JavaScript篇(下)

目录 前言 介绍 基本使用 关键帧 KeyframeEffect的三种类的声明 keyframes options 动画对象 全局Animation类 标签中的animate函数 总结 相关代码&#xff1a; 前言 接着上文往下介绍&#xff0c;上篇文章我们对JS原生动画和贝塞尔曲线有了一个详细的认识&#x…