目录
1. C++11简介
2. 统一的列表初始化
2.1 {}初始化
2.2 std::initializer_list
3. 声明
3.1 auto
3.2 decltype
3.3 nullptr
声明:C++11我会分几篇来讲,每一篇我都会讲几种特性。
1. 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++11的文档,大家若有需要或则感兴趣可以看看文档:
https://en.cppreference.com/w/cpp/11
小故事:
1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,C++国际标准委员会在研究C++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初这个标准叫 C++ 07。但是到06年的时候,官方觉得2007年肯定完不成C++ 07,而且官方觉得2008年可能也完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11。
2. 统一的列表初始化
2.1 {}初始化
在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定。比如:
struct Point { int _x; int _y; }; int main() { int array1[] = { 1,2,3,4,5 }; int array2[5] = { 0 }; Point p = { 1,2 }; return 0; }
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自定义的类型,使用初始化列表时,可添加等号(=),也可不添加。
struct Point { int _x; int _y; }; int x = 1; int y{ 2 }; int array1[]{ 1,2,3,4,5 }; int array2[5]{ 0 }; Point p{ 1,2 }; // C++11中列表初始化也可以适用于new表达式中 int* p2 = new int[4] {0};
创建对象时也可以使用列表初始化方式调用构造函数初始化
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(2024, 10, 1);// old style // C++11支持的列表初始化,这里会调用构造函数初始化 Date d2{ 2024,10,2 }; Date d3 = { 2024,10,3 }; return 0; }
运行截图:
我们确实可以从结果看到C++11支持的列表初始化会调用构造函数初始化。
2.2 std::initializer_list
std::initializer_list的介绍文档:
http://www.cplusplus.com/reference/initializer_list/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; }
运行截图:
std::initializer_list使用场景:
std::initializer_list一般是作为构造函数的参数,C++11对STL中的不少容器就增加 std::initializer_list作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。比如:
http://www.cplusplus.com/reference/list/list/list/
http://www.cplusplus.com/reference/vector/vector/vector/
http://www.cplusplus.com/reference/map/map/map/
http://www.cplusplus.com/reference/vector/vector/operator=/
以上链接分别是list容器的构造函数,vector容器的构造函数,map的构造函数以及vector的赋值运算符重载的文档。
示例代码:
int main() { vector<int> v = { 1,2,3,4 }; list<int> l = { 1,2,3 }; // 这里{"苹果", "apple"}会先初始化构造一个pair对象 map<string, string> m = { {"苹果","apple"},{"香蕉","banana"} }; // 使用大括号对容器赋值 v = { 10, 20, 30 }; return 0; }
模拟实现的vector也支持{}初始化和赋值
namespace kuruomi { template<class T> class vector { public: typedef T* iterator; vector(initializer_list<T> l) { _start = new T[l.size()]; _finish = _start + l.size(); _endofstorage = _start + l.size(); iterator vit = _start; //在模板编程中,当你需要在模板定义中引用依赖于模板参数的类型时,就必须使用 typename。 //因为编译器无法确认此处的iterator是类型还是变量,不加的话编译器会报错。 typename initializer_list<T>::iterator lit = l.begin(); while (lit != l.end()) { *vit++ = *lit++; } //for (auto e : l) // *vit++ = e; } 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; }; } int main() { kuruomi::vector<int> v = { 1,2,3,4 }; v = { 10, 20, 30 }; return 0; }
这三个成员变量分别是数组的开头、结尾、空间边界,这里我们就是利用initializer_list参数进行初始化,用迭代器的方式进行拷贝,赋值运算符重载就是复用了拷贝构造。
3. 声明
c++11提供了多种简化声明的方式,尤其是在使用模板时。
3.1 auto
在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将其用于实现自动类型判断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初始化值的类型。
int main() { int i = 10; auto p = &i; auto pf = strcpy; cout << typeid(p).name() << endl; cout << typeid(pf).name() << endl; map<string, string> dict = { {"sort", "排序"}, {"insert", "插入"} }; //map<string, string>::iterator it = dict.begin(); auto it = dict.begin(); cout << typeid(it).name() << endl; return 0; }
运行截图:
3.2 decltype
关键字decltype将变量的类型声明为表达式指定的类型。
template<class T1,class T2> void F(T1 t1,T2 t2) { decltype(t1 * t2) ret; cout << typeid(ret).name() << endl; }; int main() { const int x = 1; double y = 2.2; decltype(x * y) ret; // ret的类型是double decltype(&x) p; // p的类型是int* cout << typeid(ret).name() << endl; cout << typeid(p).name() << endl; F(1, 'a'); return 0; }
运行截图:
3.3 nullptr
由于C++中NULL被定义成字面量0,这样就可能回带来一些问题,因为0既能指针常量,又能表示整形常量。所以出于清晰和安全的角度考虑,C++11中新增了nullptr,用于表示空指针。