ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

C++:C++11

C++:C++11 前言:前面涉及C++11的知识点我们补充讲解过,现在这篇博客我们正式讲解C++11。我尽量活成我想成为的模样,而不是某些人口中的‘他’。我们每个人都在跟自己的业力对抗,都想努力变好,可是这个世界本身就是矛盾的。愿吾之愿力能护持汝生生世世,不被世俗沉沦,看破诸法实相C++11简介在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了 C++98称为C++11之前的最新C++标准名称。不过由于C++03(TC1)主要是对C++98标准中的漏洞 进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。相比C++98/03,C++11则带来了数量可观的变化,其中包含了约140个新特性,以及对C++03标准中约600个缺陷的修正,这使得C++11更像是从C++98/03中孕育出的一种新语言。相比较而言,C++11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个重点去学习。C++11增加的语法特性非常篇幅非常多,我们这里没办法一 一讲解,所以本节课程主要讲解实际中比较实用的语法。统一的列表初始化{}初始化在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如:C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。创建对象时也可以使用列表初始化方式调用构造函数初始化class Date { public: Date(int year, int month, int day) :_year(year) ,_month(month) ,_day(day) { cout "Date(int year, int month, int day)" endl; } private: int _year; int _month; int _day; }; int main() { Date d1(2022, 1, 1); // old style // C++11支持的列表初始化,这里会调用构造函数初始化 Date d2{ 2022, 1, 2 }; //直接调用构造 Date d3 = { 2022, 1, 3 }; //这里先调用构造生成一个初始化的临时对象,然后拷贝构造给对象 return 0; }单参数列表初始化可省略列表class A { public: //explicit A(int x, int y) explicit可以防止隐式类型转换,使列表初始化失效 A(int x, int y) :_x(x) ,_y(y) {} A(int x) :_x(x) ,_y(x) {} private: int _x; int _y; }; int main() { A aa1 = 1; A aa2={1}; //都是先调用构造生成临时对象,然后拷贝构造 return 0; }赋值重载运算符也支持使用列表,A a,a={1}或a=1,先构造临时对象然后调用赋值运算符。std::initializer_liststd::initializer_list是什么类型std::initializer_list使用场景std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器就增加std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。没有initializer_list赋值重载:a={1}→ 构造临时对象,再拷贝赋值。有initializer_list赋值重载:a={1}→ 直接接收列表,无临时对象。int main() { vectorint v = { 1,2,3,4 }; //vectorint v{ 1,2,3,4 }; //vectorint v({1,2,3,4}); listint lt = { 1,2 }; // 1.这里{"sort", "排序"}会先初始化构造一个pair对象 // 2.initializer_listpair的构造 mapstring, string dict = { {"sort", "排序"}, {"insert", "插入"} }; // 使用大括号对容器赋值 v = {10, 20, 30}; return 0; }让模拟实现的vector也支持{}初始化和赋值namespace bit { templateclass T class vector { public: typedef T* iterator; vector(initializer_listT l) { _start = new T[l.size()]; _finish = _start + l.size(); _endofstorage = _start + l.size(); iterator vit = _start; typename initializer_listT::iterator lit = l.begin(); while (lit != l.end()) { *vit++ = *lit++; } //for (auto e : l) // *vit++ = e; } vectorT operator=(initializer_listT l) { vectorT 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; }; }声明c++11提供了多种简化声明的方式,尤其是在使用模板时。auto在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。具体看前文。decltype关键字decltype将变量的类型声明为表达式指定的类型。templateclass T class B { public: T* New(int n) { return new T[n]; } }; auto func1() { listint lt; auto ret = lt.begin(); return ret; } int main() { listint::iterator it1; // typeid推出时一个单纯的字符串 cout typeid(it1).name() endl; // // 不能用来定义对象 // //typeid(it1).name() it2; // 可以用来定义对象 decltype(it1) it2; cout typeid(it2).name() endl; auto it3 = it1; cout typeid(it3).name() endl; auto ret3 = func1(); Bdecltype(ret3) bb1; Bdecltype(it3) bb2; //Bstd::listint::iterator bb2; // auto和decltype有些地方增加代码读起来难度 return 0; }nullptr由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示 整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针范围for循环在前文我们已经具体讲解了,这里我们补充一点int main() { mapstring, string dict2 = { {"sort", "排序"}, {"insert", "插入"} }; for (auto [x,y] : dict2) { cout x ":" y endl; //x += '1'; y += '2'; } for (auto [x, y] : dict2) { cout x ":" y endl; } pairstring, string kv1 = { "sort", "排序" }; auto [x, y] = kv1; return 0; }智能指针这个我们在智能指针课程中已经会进行了非常详细的讲解,这里就不进行讲解了STL中一些变化新容器用橘色圈起来是C++11中的一些几个新容器,但是实际最有用的是unordered_map和 unordered_set。这两个我们前面已经进行了非常详细的讲解,其他的大家了解一下即可。容器中的一些新方法如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。比如提供了cbegin和cend方法返回const迭代器等等,但是实际意义不大,因为begin和end也是可以返回const迭代器的,这些都是属于锦上添花的操作。实际上C++11更新后,容器中增加的新方法最后用的插入接口函数的右值引用版本:http://www.cplusplus.com/reference/vector/vector/emplace_back/http://www.cplusplus.com/reference/vector/vector/push_back/http://www.cplusplus.com/reference/map/map/insert/http://www.cplusplus.com/reference/map/map/emplace/右值引用和移动语义左值引用和右值引用传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。什么是左值?什么是左值引用?左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。什么是右值?什么是右值引用?右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引 用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地址,也可以修改rr1。如果不想rr1被修改,可以用const int rr1去引用。我的总结:左值:可以取地址的右值:不可以取地址的,分为内置类型字面常量,匿名对象/变量,临时对象/变量左值和右值都存在地址,只是左值能取地址,而右值不能取地址右值分为纯右值(字面常量)和将亡值(匿名对象/变量,临时对象/变量)个人猜测:右值引用引用字面常量时,会生成临时变量/对象int p=10;// int b=10; int p= b; b是临时变量class A{public: A(int a1, int a2) :_a1(a1) , _a2(a2) {}private: int _a1; int _a2;};A p= { 1,2 };// A b(1,2); A p= b; b是临时对象左值引用与右值引用比较左值引用总结:1.左值引用只能引用左值,不能引用右值。2.但是const左值引用既可引用左值,也可引用右值
返回列表