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.mzph.cn/news/27710.shtml

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

相关文章

影响亚马逊Listing转化率的14大因素你知道吗?

我们都知道亚马逊listing转化率对于链接的推新和维稳来说有多么重要&#xff0c;只要转化率的比值无法达到整体市场平均比值的及格线&#xff0c;你就很可能会慢慢被亚马逊的飞轮算法所淘汰。 那么&#xff0c;具体是哪些因素在影响着你的listing转化率呢?这里我们可以分为显…

wm8960没有声音

最近在imx6ull上调试这个声卡&#xff0c;用官方的镜像是能发声的&#xff0c;换到自己做的镜像上&#xff0c;就没有声音。 记录一下过程&#xff1a; 内核和设备树。只要有下面的显示&#xff0c;就说明加载成功。 再看一下aplay的显示 到此&#xff0c;驱动都是正常的。但…

数学建模-元胞自动机

clc clear n 300; % 定义表示森林的矩阵大小 Plight 5e-6; Pgrowth 1e-2; % 定义闪电和生长的概率 UL [n,1:n-1]; DR [2:n,1]; % 定义上左&#xff0c;下右邻居 vegzeros(n,n); % 初始化表示森林的矩阵 imh ima…

适配器模式-java实现

意图 复用已经存在的接口&#xff0c;与所需接口不一致的类。即将一个类&#xff08;通常是旧系统中的功能类&#xff09;&#xff0c;通过适配器转化成另一个接口的实现。&#xff08;简单来说&#xff0c;就是复用旧系统的功能&#xff0c;去实现新的接口&#xff09; 我们举…

在R中比较两个矩阵是否相等

目录 方法一&#xff1a;使用all.equal()比较两个R对象是否近似相等 方法二&#xff1a;使用identical比较两个R对象是否精确相等。 方法一&#xff1a;使用all.equal()比较两个R对象是否近似相等 使用函数&#xff1a;all.equal(x,y) 比较两个R对象x和y是否近似相等 > M1…

DEVICENET转ETHERCAT网关连接西门子支持ethercat吗

你有没有遇到过生产管理系统中&#xff0c;设备之间的通讯问题&#xff1f;两个不同协议的设备进行通讯&#xff0c;是不是很麻烦&#xff1f;今天&#xff0c;我们为大家介绍一款神奇的产品&#xff0c;能够将不同协议的设备进行连接&#xff0c;让现场的数据交换不再困扰&…

嵌入式开发学习(STC51-18-LCD液晶显示)

内容 在LCD1602液晶上显示字符信息&#xff1b; LCD1602介绍 简介 1602液晶也叫1602字符型液晶&#xff0c;它能显示2行字符信息&#xff0c;每行又能显示16个字符&#xff1b; 它是一种专门用来显示字母、数字、符号的点阵型液晶模块&#xff1b; 它是由若干个5x7或者5x…

WSL2安装CentOS7和CentOS8

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、下载ZIP包&#xff1f;二、安装1.打开Windows子系统支持2.安装到指定位置3.管理虚拟机4.配置虚拟机1.配置国内源2.安装软件3.安装第三方源 5.配置用户1.创建…

redis 集群 1:李代桃僵 —— Sentinel

目前我们讲的 Redis 还只是主从方案&#xff0c;最终一致性。读者们可思考过&#xff0c;如果主节点凌晨 3 点突发宕机怎么办&#xff1f;就坐等运维从床上爬起来&#xff0c;然后手工进行从主切换&#xff0c;再通知所有的程序把地址统统改一遍重新上线么&#xff1f;毫无疑问…

使用 Splashtop Secure Workspace 改进安全访问

前言&#xff1a;Splashtop 首席执行官兼联合创始人 Mark Lee 我们在十多年前推出 Splashtop 远程访问和支持产品线时&#xff0c;专注于为用户提供高性能和卓越的用户体验&#xff0c;以便用户能够随处访问计算资源。如今&#xff0c;我们有25万企业客户和3000万个人用户&…

机器学习深度学习——卷积的多输入多输出通道

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——从全连接层到卷积 &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文章对你们有所帮…

C++三个线程依次打印abc

代码 #include<iostream> #include<thread> #include<mutex> #include<condition_variable> using namespace std; mutex mtx; condition_variable cv; int flag0; void A(){unique_lock<mutex>lk(mtx);int count0;while(count<10){while(fl…

Jmeter录制HTTPS脚本

Jmeter录制HTTPS脚本 文章目录 添加“HTTP代理服务器”设置浏览器代理证书导入存在问题 添加“HTTP代理服务器” 设置浏览器代理 保持端口一致 证书导入 点击一下启动让jmeter自动生成证书&#xff0c;放在bin目录下&#xff1a; 打开jmeter的SSL管理器选择刚刚生成的证书&…

Vue——formcreate表单设计器自定义组件实现(二)

前面我写过一个自定义电子签名的formcreate表单设计器组件&#xff0c;那时初识formcreate各种使用也颇为生疏&#xff0c;不过总算套出了一个组件不是。此次时隔半年又有机会接触formcreate&#xff0c;重新熟悉和领悟了一番各个方法和使用指南。趁热打铁将此次心得再次分享。…

THS4301 振荡问题排查及解决过程

项目背景简介: 本项目是基于一款微弱信号处理前级模拟电路设计方案。 问题描述: 在生产标定中,发现以前的程序在小量程标定后,切换到差分和单端后,两者的直流偏置不一样,且切换到差分输入时,能发现有振荡现象(有设备单端输入也有振荡); 排查分析过程: 1)首先可以…

tomcat上部署jpress

一.确保有jdk&#xff0c;tomcat和mysql环境 二.新建jpress数据库&#xff0c;新建jpress用户并赋予所有权限 三.将jpress的war上传到tomcat/apache-tomcat-8.5.70/webapps&#xff0c;具体根据你的实际tomcat安装路径为准&#xff0c;上传完成后他会自己解包 四.到浏览器完…

JAVA实现图书管理系统(思路,和完整代码)

因为文件过多每个文件之间的关系如下&#xff08;每个文件中都只有一个类&#xff09;&#xff1a; 因为JAVA属于面向对象编程的语言&#xff0c;所以我们想要实现图书管理系统就得分以下几步&#xff1a; 找出其中的所有的对象实现所有的对象完成对象之间的交互 在图书管理系…

【产品经理】高阶产品如何提出有效解决方案?(1方法论+2案例+1清单)

每一件事情总有它的解决方案&#xff0c;在工作中亦是如此&#xff0c;而有效的解决方案&#xff0c;一定是具有系统性的。 有效的解决方案&#xff0c;一定是系统性的解决方案。 什么是系统性解决方案&#xff1f; 从系统结构&#xff08;或连接关系&#xff09;入手&#x…

【C语言】初识C语言+进阶篇导读

✨个人主页&#xff1a; Anmia.&#x1f389;所属专栏&#xff1a; C Language &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 本篇目的是面向编程新手&#xff0c;没接触过编程的人。以及C进阶的导读。 内容是C语言重要知识点的简单解释&#xff0c;不做详解。给…

73. 矩阵置零

题目链接&#xff1a;力扣 解题思路&#xff1a; 方法一&#xff1a;比较容易想到的方向&#xff0c;使用两个数组row和col保存有0的行或者列&#xff0c;然后将有0的那一行或那一列的所有元素都设置为0 AC代码 class Solution {public void setZeroes(int[][] matrix) {in…