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()
{
	// decltype
	int 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.mfbz.cn/a/63993.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python连接Hive实例教程

一 Python连接hive环境实例 经在网络查询相关的教程&#xff0c;发现有好多的例子&#xff0c;发现连接底层用的的驱动基本都是pyhive和pyhs2两种第三方库的来连接的 hive,下面将简介windows 10 python 3.10 连接hive的驱动程序方式&#xff0c;开发工具&#xff1a;pycharm …

layui之layer弹出层的icon数字及效果展示

layer的icon样式 icon如果在信息提示弹出层值(type为0)可以传入0-6&#xff0c;icon与图标对应关系如下&#xff1a; 如果是加载层&#xff08;type为3&#xff09;可以传入0-2&#xff0c;icon与图标对应关系如下&#xff1a;

基于fpga的电子时钟

文章目录 前言实验手册一、实验目的二、实验原理1&#xff0e;理论原理2&#xff0e;硬件原理 三、系统架构设计四、模块说明1&#xff0e;模块端口信号列表按键消抖模块&#xff08;key&#xff09;计数器模块&#xff08;counter&#xff09;蜂鸣器乐谱模块(music)蜂鸣器发声…

有没有好用的在线画图工具推荐?

绘画是设计师最常见的工作之一&#xff0c;设计师对在线绘画工具的要求越来越高&#xff0c;市场上也出现了各种在线绘画工具&#xff0c;让设计师不知道如何选择高质量的在线绘画工具&#xff0c;一个好的在线绘画工具不仅可以让你轻松绘画&#xff0c;而且可以让你的工作效率…

可视化高级绘图技巧100篇-总论

前言 优秀的数据可视化作品可以用三个关键词概括&#xff1a;准确、清晰、优雅。 准确&#xff1a;精准地反馈数据的特征信息&#xff08;既不遗漏也不冗余&#xff0c;不造成读者疏漏&误读细节&#xff09; 清晰&#xff1a;获取图表特征信息的时间越短越好 优雅&…

【ARM64 常见汇编指令学习 13 -- ARM 汇编 ORG 伪指令学习】

文章目录 ARM ORG 指令介绍UEFI 中对 ORG 指令的使用 ARM ORG 指令介绍 在ARM汇编中&#xff0c;"org"是一个汇编器伪指令&#xff0c;用于设置下一条指令的装入地址。"org"后面跟着的是一个表达式&#xff0c;这个表达式的值就是下一条指令的装入地址。如…

21、p6spy输出执行SQL日志

文章目录 1、背景2、简介3、接入3.1、 引入依赖3.2、修改database参数&#xff1a;3.3、 创建P6SpyLogger类&#xff0c;自定义日志格式3.4、添加spy.properties3.5、 输出样例 4、补充4.1、参数说明 1、背景 在开发的过程中&#xff0c;总希望方法执行完了可以看到完整是sql语…

侯捷 C++面向对象编程笔记——9 复合 委托

9 复合 委托 9.1 Composition 复合 类似于c中结构里有结构——class里有class deque 是一个已经存在的功能很多的类&#xff08;两头进出的队列&#xff09;&#xff1b;利用deque的功能来实现queue的多种操作 该例只是复合的一种情况——设计模式 Adapter 9.1.1 复合下的构造…

C# 完成串口通信RS485

C# 完成串口通信RS485|RS232上下位机交互 第零步&#xff1a; 我用的是电脑usb 转串口的所以首先是驱动程序下载&#xff0c;我们用的是CH341 下载地址&#xff1a;https://www.wch.cn/downloads/CH341SER_EXE.html 第一步&#xff1a;连接机器 RS485 上面有三个端子&#xf…

【Python】Locust持续优化:InfluxDB与Grafana实现数据持久化与可视化分析

目录 前言 influxDB 安装运行InfluxDB 用Python 上报数据到influxdb ocust 数据写入到 influx Locust的生命周期 上报数据 优化升级 配置Grafana 总结 资料获取方法 前言 在进行性能测试时&#xff0c;我们需要对测试结果进行监控和分析&#xff0c;以便于及时发现问…

flutter-GridView使用

先看效果 代码实现 import package:app/common/util/k_log_util.dart; import package:app/gen/assets.gen.dart; import package:app/pages/widget/top_appbar.dart; import package:flutter/cupertino.dart; import package:flutter/material.dart; import package:flutter_…

使用idea实现git操作大全(在项目开发中遇到的实际情况

使用idea实现git操作大全&#xff08;在项目开发中遇到的实际情况&#xff09; 1.安装git插件2.在开发中切记拉一个自己的分支 1.安装git插件 2.在开发中切记拉一个自己的分支 选中需要拉的分支&#xff0c;右键该分支&#xff0c;选中new breach from “分支”&#xff0c;点…

《数据中台实践指南(1.0 版)》发布,大数据技术标准推进委员会、联合行业专家、头部企业共同编制

导读 大数据技术标准推进委员会牵头&#xff0c;联合行业专家和头部企业共同编制《数据中台实践指南&#xff08;1.0 版&#xff09;》&#xff0c;梳理数据中台历史及概念&#xff0c;明确数据中台的核心能力&#xff0c;总结数据中台建设的前提条件和不同形态&#xff0c;给…

csdn崩溃了?每次都卡

反馈给了官方客服也没有响应&#xff0c;最近几周都是这样的高频率的转圈圈&#xff01;这个入口不受重视&#xff1f;这个对于csdn用户来说&#xff0c;是最最基本的入口 如果CSDN&#xff08;CSDN.net&#xff09;崩溃了&#xff0c;可能会对以下方面产生影响&#xff1a; 开…

Docker Compose构建lnmp

目录 Compose的优点 编排和部署 Compose原理 Compose应用案例 安装docker-ce 阿里云镜像加速器 安装docker-compose docker-compose用法 Yaml简介 验证LNMP环境 Compose的优点 先来了解一下我们平时是怎么样使用docker的&#xff1f;把它进行拆分一下&#xff1a; 1…

任务14、无缝衔接,MidJourney瓷砖(Tile)参数制作精良贴图

14.1 任务概述 在这个实验任务中,我们将深入探索《Midjourney Ai绘画》中的Tile技术和其在艺术创作中的具有挑战性的应用。此任务将通过理论学习与实践操作相结合的方式,让参与者更好地理解Tile的核心概念,熟练掌握如何在Midjourney平台上使用Tile参数,并实际运用到AI绘画…

运维作业—5

一.基于 CentOS 7 构建 LVS-DR 群集 1.配置LVS 2.第一台real server&#xff08;192.168.100.139:80&#xff09; 手工在RS端绑定VIP 手工在RS端抑制ARP响应 3.第二台real server&#xff08;192.168.100.140:80&#xff09; 安装arptables并启动 使用arptables实现抑制 测试…

解决Vs Code工具开发时 保存React文件时出现乱码情况

Vs Code工具开发时 保存React文件时出现乱码情况 插件库搜索:JS-CSS-HTML Formatter 把这个插件禁用或者卸载就解决保存时出现乱码的问题了; 如果没有解决,再看下面方案! 出现乱码问题通常是因为文件的编码格式不正确。您可以尝试以下解决方法&#xff1a; 确认文件编码格式&a…

以太网帧格式与吞吐量计算

以太网帧结构 帧大小的定义 以太网单个最大帧 6&#xff08;目的MAC地址&#xff09; 6&#xff08;源MAC地址&#xff09; 2&#xff08;帧类型&#xff09; 1500{IP数据包[IP头&#xff08;20&#xff09;DATA&#xff08;1480&#xff09;]} 4&#xff08;CRC校验&#xff…

MySQL(1)

MySQL创建数据库和创建数据表 创建数据库 1. 连接 MySQL mysql -u root -p 2. 查看当前的数据库 show databases; 3. 创建数据库 create database 数据库名; 创建数据库 4. 创建数据库时设置字符编码 create database 数据库名 character set utf8; 5. 查看和显示…
最新文章