C++11 -- 初识

目录

  • C++11简介
  • 1、初始化列表
    • 1.1、初始化
    • 1.2、std::initializer_list(初始化列表类)
  • 2、类型说明/指示符
    • 2.1、auto
    • 2.、decltype
  • 3、nullptr
  • 4、范围for语句
  • 5、STL新增的容器

前言
本篇文章进行C++11的学习!!!


C++11简介

  1. C++11相比C++98和03而言,它带来了数量可观的变化,其中包含了140个特性,以及对C++03标准中600个缺陷的修正,这使得C++11更像是从 C++98和03中孕育出的一种新语言

  2. C++11能够更好的用于系统开发和库开发,语法更加泛化和简单化,更加稳定安全,不仅功能强大,而且能够提升程序员的开发效率,公司实际项目也用的比较多,我们需要作为重点去学习它…

C++11特性在编译器的契合度列表:链接


1、初始化列表

1.1、初始化

  • 在C++98中,允许使用{}对数组和结构体进行初始化赋值,例如:
struct T
{
	int a;
	double b;
	char c;
};

void Test()
{
	int Array[] = { 1, 2, 3, 4, 5 };
	int Array[10] = { 0 };
	T t = { 1, 1.0, 'a' };
}

  • C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加
class Date
{
public:
	Date(size_t _year, size_t _month, size_t _day)
		: year(_year)
		, month(_month)
		, day(_day)
	{}
private:
	size_t year;
	size_t month;
	size_t day;
};

void Test_Init()
{
	int a = { 0 };
	int aa{ 0, 1, 2, 3, 4 };

	int Array[]{ 0 }

	// 初始化列表的应用 -- 对自定义类型,调用其构造函数进行初始化
	vector<int> v{ 1, 2, 3, 4, 5 };
	list<string> l{ "abc", "def", "hlj" };

	int* pa = new int{ 0 };
	int* pb = new int[10]{ 0, 1, 2, 3, 4 };
	
	// 自动调用Date的构造函数进行初始化
	Date* pd = new Date{ 2022, 9, 21 };
	Date* ppd = new Date[3]{ {2022, 9, 21}, {2022, 9, 22}, {2022, 9, 23} };
	
	// 键对值也是如此,因为map的底层pair也是一个模板,所以要套二个{}
	map<string, string> dict { {"string", "字符串"} };
	map<string, string> dict2 { {"left", "左边"}, {"right", "右边"}};
}

初始化列表的优点:

  • C++98使用new构建一段连续空间时,不能对其进行初始化,而C++11后就解决了该问题

  • C++98对自定义类型进行初始化时,只能初始化一次,而C++11可以批量初始化

缺点:

  • 使用起来让人有些别扭,虽然是为了让C++用的更舒服一些,特别是内置类型初始化时

1.2、std::initializer_list(初始化列表类)

【std::initializer_list文档】


std::initializer_list是什么类型:

int main()
{
	// the type of il is an initializer_list
	auto il = { 10, 20, 30 };
	cout << typeid(il).name() << endl;
	return 0;
}

运行结果:class std::initializer_list


std::initializer_list使用场景:

  • std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器就增加

  • std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值

list构造函数
C++11 -- 初识_第1张图片

vector构造函数
C++11 -- 初识_第2张图片

map构造函数

C++11 -- 初识_第3张图片

vector赋值拷贝
C++11 -- 初识_第4张图片

int main()
{
	vector<int> v = { 1, 2, 3, 4, 5 };
	list<int> lt = { 1, 2, 3, 4, 5 };
	
	// 这里{"sort", "排序"}会先初始化构造一个pair对象
	map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} };
	
	// 使用初始化列表对vector进行赋值拷贝操作
	v = {10, 20, 30};
	return 0;
}

模拟实现vector也支持使用{}初始化(简版):

namespace myvector
{
	template<class T>
	class vector 
	{
	public:
		typedef T* iterator;
		
		// 套了一层初始化列表类,使用它的迭代器逐个赋值给vector
		vector(initializer_list<T> l)
		{
			_start = new T[l.size()];
			_finish = _start + l.size();
			_endofstorage = _start + l.size();
			iterator vit = _start;
			typename initializer_list<T>::iterator lit = l.begin();
			while (lit != l.end())
			{
				*vit++ = *lit++;
			}
		}

		// 这里复用了vector的构造函数
		vector<T>& operator=(initializer_list<T> l) 
		{
			vector<T> tmp(l);
			std::swap(_start, tmp._start);
			std::swap(_finish, tmp._finish);
			std::swap(_endofstorage, tmp._endofstorage);
			return *this;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

2、类型说明/指示符

2.1、auto

简介:

  • 在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了

  • C++11中废弃auto原来的用法,将其用于实现自动类型推导。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型

void Test_auto()
{
	// 普通类型
	int a = 10;
	auto b = a;
	cout << typeid(b).name() << endl;
	
	// 指针类型
	auto pa = &a;
	cout << typeid(pa).name() << endl;

	// 常量普通类型
	const int ca = 10;
	auto cb = ca;
	cout << typeid(cb).name() << endl;
	
	// 常量指针类型
	auto pcb = &ca;
	cout << typeid(pcb).name() << endl;
	
	// 引用类型
	int d = 10;
	int& pd = d;
	auto ppd = pd;
	cout << typeid(ppd).name() << endl;

	// 常量引用类型
	const int& pq = 10;
	auto ppq = pq;
	cout << typeid(ppq).name() << endl;

	// 注意:auto虽然支持连续推导,但是类型必须一致
	auto p1 = 10, p2 = 20, p3 = 30;		// 可以
	auto x1 = 1, x2 = 1.0, x3 = '\0'; 	// error
}

结论:对常量内置/自定义类型进行推导时,会忽略常量的属性,只有常量指针类型才不会忽略其常量属性,但是指针常量的const属性也会被忽略
C++11 -- 初识_第5张图片


2.、decltype

  • 关键字decltype将变量的类型声明为表达式指定的类型。在这一过程中,编译器分析表达式并得到它的类型,但不实际计算表达式的值

例子1:

void Test_decltype()
{
	// 关键字decltype将变量的类型声明为表达式指定的类型
	int a = 10, &pa = a;
	decltype(a) a2 = 20;		// int
	decltype(pa) pa2 = a;		// int&
	decltype(&a) pa2 = &a;		// int*

	const int b = 20, &pb = b;
	decltype(b) b2 = b;			// const int
	decltype(pb) pb3 = pb;		// const int&
	decltype(&b) pb4 = &b;		// const int*

	decltype(a + 0) c = 30;		// int	

	const Date d1(1, 1, 1);
	decltype(d1) d2(d1);		// const Date
}

结论:decltype类型说明符可以推导常量属性的内置类型和自定义类型


例子2:

void Test_decltype2()
{
	// 关键字decltype将变量的类型声明为表达式指定的类型
	int i = 20, *p = &i, &pi = i;
	decltype(*p) pi2 = i;		// pi2的类型是int&,因为解引用p后拿到的是左值,可修改
	decltype(*p) pi3;			// error,引用必须初始化
}

例子3:

void Test_decltype3()
{
	int i = 10;
	decltype((i)) pi = i;		// int&

	const int ci = 20;
	decltype((ci)) pci = 30;	// const int&
}

结论:无论什么类型,在decltype的推导中加上()时,都会被识别成引用类型


3、nullptr

  • 由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

具体的问题我在C++初识的章节中有讲到,请转到【C++初识(四)】


4、范围for语句

简介:C++11引入了一种更简单的for语句,这种语句可以遍历容器和其他序列的所有元素,语法:

for (declaration : expression)
{
	statement
}

语法解析:

  • expression:表示必须必须是一个序列,序列可以是{}括起来的初始值列表,数组,序列容器(vector, list, sting等等)关联式容器(set, map),它们共同点是都拥有返回begin()和end()的迭代器

  • declaration:表示定义一个变量,保证序列中每个元素都能转换成对应的变量类型

  • statement:迭代内容,一般显示输出该序列中元素的值

void Test_For()
{
	// 初始化列表
	for (auto& e : { 0, 1, 2, 3, 4 })
	{
		cout << e << " ";
	}
	cout << endl;
	
	// 序列
	int Array[] = { 10, 20, 30, 40, 50 };
	for (auto& e : Array)
	{
		cout << e << " ";
	}
	cout << endl;
	
	// 序列容器
	vector<int> v{ 0, 1, 2, 3, 4, 5 };
	for (auto& e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	
	// 关联式容器
	map<string, string> dict{ {"left", "左边"}, {"right", "右边"} };
	for (auto& kv : dict)
	{
		cout << kv.first << ": " << kv.second << endl;
	}
	cout << endl;
}

C++11 -- 初识_第6张图片

结论:其实范围for底层会转换成使用迭代器进行遍历,范围for并没有对遍历进行优化


5、STL新增的容器

新容器:

  • 用橘色框框圈起来是C++11中的一些几个新容器,但是实际最有用的是unordered_map和unordered_set。这两个我们前面已经进行了非常详细的讲解,其他的大家了解一下即可

C++11 -- 初识_第7张图片

  • 容器中的一些新方法
  1. 如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的

  2. 比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作

  3. 实际上C++11更新后,容器中增加的新方法最后用的插入接口函数的右值引用版本:

【vector右值引用插入版本】
【vector中push_back的右值引用版本】
【map中insert的右值引用版本】
【map中的右值引用插入版本】

这些接口都母庸置疑的提高了许多效率,但是效率是怎么提高的,等下学习右值引用就知道了!!!

你可能感兴趣的:(C++和高阶数据结构,c++,c语言,算法)