目录
一、C++11简介
二、列表初始化
2.1{}初始化
2.2std::initializer_list
2.2.1原理
2.2.2使用场景
三、声明
3.1auto && typeid().name()
3.2decltype
一、C++11简介
小故事:
1998年是C++标准委员会成立的第一年,本来计划以后每5年实际需要更新一次标准,C++国际标准委员会在研究C++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初标准叫C++ 07。但是到06年的时候,官方觉得2007年坑定完不成C++ 07,而且官方觉得2008年可能也玩不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是其他年完成。结果2010年的时候也没完成,最后在11年终于完成了C++标准。所以最终定名为C++ 11。
C++11相对98/03,带来了数量可观的变化,其中包含约140新特性,以及对03标准约600个缺陷的修正,其次,11能更好地用于系统开发和库开发、语法更加范化和简单化、更加稳定和安全,不仅功能更强大、而且能提升程序员的开发效率,企业中项目开发中也用得比较多,所以需要重点学习。C++11的特性很多,只需挑重点进行学习。
二、列表初始化
2.1{}初始化
在c++98中,标准规定{}只能对数组或者结构进行初始化。
而在c++11中,{}不仅兼容98的规定,还可以对内置类型和自定义类型进行初始化,可添加等号(=),也可不添加。例子:
内置类型:
#include <iostream>using namespace std;
int main()
{int a{ 1 };int b = { 3 };cout << a << endl << b << endl;int arr[]{ 1,2,3,4,5 };int brr[] = { 32,3,5,42,1 };for (auto e : arr){cout << e;}cout << endl;for (auto e : brr){cout << e;}return 0;
}
输出结果:
自定义类型:
#include <iostream>using namespace std;
class Date
{
public:Date(int year, int month, int day):_year(year), _month(month), _day(day){}void Print(){cout << _year << ":" << _month << ":" << _day << endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d{ 2024,6,12 };d.Print();Date d2 = { 2024,6,12 };d2.Print();return 0;
}
输出结果:
2.2std::initializer_list
在学习过的c++98中,所谓的成员初始化赋值,走的是初始化列表, 实则最终调用的是成员的构造函数进行初始化,在C++11中,不仅可以兼容98的初始化构造,还给出了属于自己的初始化列表,该初始化列表是一个类模板,内部由两个指针维护着,还有些版本是由一个指针和一个变量维护,当然我们学习的是官方的两个指针的版本。当使用{}进行初始化时,走的就是c++11的初始化列表。
初始化列表支持以下操作:
2.2.1原理
那么当使用{}进行初始化时具体是如何做的?
若是两个指针的版本,使用初始化列表时编译器会生成一个临时数组来存储初始化列表中的元素,并将这个数组的起始位置和最后一个元素的下一个位置指针和大小传递给std::initializer_list
的构造函数,这些步骤由编译器完成。如下:初始化列表部分源码
//初始化列表部分源码
template <class _Elem>
class initializer_list {
public:using value_type = _Elem;using reference = const _Elem&;using const_reference = const _Elem&;using size_type = size_t;using iterator = const _Elem*;using const_iterator = const _Elem*;constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) {}constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept: _First(_First_arg), _Last(_Last_arg) {}_NODISCARD constexpr const _Elem* begin() const noexcept {return _First;}_NODISCARD constexpr const _Elem* end() const noexcept {return _Last;}_NODISCARD constexpr size_t size() const noexcept {return static_cast<size_t>(_Last - _First);}private:const _Elem* _First;const _Elem* _Last;
};
若是一个指针,一个变量的版本,使用初始化列表时编译器会生成一个临时数组来存储初始化列表中的元素,并将这个数组的起始位置和数组的大小传递给std::initializer_list
的构造函数,这些步骤由编译器完成。此外,如下:初始化列表部分源码
template<class _E>class initializer_list{public:typedef _E value_type;typedef const _E& reference;typedef const _E& const_reference;typedef size_t size_type;typedef const _E* iterator;typedef const _E* const_iterator;private:iterator _M_array;size_type _M_len;// The compiler can call a private constructor.constexpr initializer_list(const_iterator __a, size_type __l): _M_array(__a), _M_len(__l) { }public:constexpr initializer_list() noexcept: _M_array(0), _M_len(0) { }// Number of elements.constexpr size_typesize() const noexcept { return _M_len; }// First element.constexpr const_iteratorbegin() const noexcept { return _M_array; }// One past the last element.constexpr const_iteratorend() const noexcept { return begin() + size(); }};template<class _Tp>constexpr const _Tp*begin(initializer_list<_Tp> __ils) noexcept{ return __ils.begin(); }template<class _Tp>constexpr const _Tp*end(initializer_list<_Tp> __ils) noexcept{ return __ils.end(); }
2.2.2使用场景
由于C++11初始化列表的出现,STL容器也发生了变化,有了新的初始化。如下:
在这里介绍一下vector和map的初始化列表:
vector:
map:
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{vector<int> v{ 1,2,3,4 };for (auto e : v){cout << e << " ";}cout << endl;v = { 3,4,22,2,9 };for (auto e : v){cout << e << " ";}cout << endl;map<string, string> m{ {"apple","苹果"},{"banana","香蕉"},{"string","字符串"} };for (auto& e : m){cout << e.first << ":" << e.second << endl;}m = { {"I","我"},{"love","爱"},{"you","你"} };//等号可省略for (auto& e : m){cout << e.first << ":" << e.second << endl;}return 0;
}
输出结果:
在这里为了更好的对初始化列表有一个了解,用其来实现一下vector的初始化:
#include <initializer_list>
#include <iostream>using namespace std;namespace bit
{template<class T>class vector{public:typedef T* iterator;vector(initializer_list<T> lt){_start = new T[lt.size()];_finish = _start + lt.size();_endofstorage = _start + lt.size();iterator st = _start;typename initializer_list<T>::iterator it = lt.begin();while (it != lt.end()){*st++ = *it++;}}vector<T>& operator=(initializer_list<T> lt){vector<T> temp(lt);std::swap(_start,temp._start);std::swap(_finish, temp._finish);std::swap(_endofstorage, temp._endofstorage);return *this;}private:T* _start;T* _finish;T* _endofstorage;};}
三、声明
c++11提供了多种简化声明的方式,尤其是在使用模板时。
3.1auto && typeid().name()
想必auto都已经很熟悉了,在前面就经常使用,在c++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,大意就是变量具有局部属性,拥有自动生命周期,但是不加该说明符,局部域中定义局部的变量默认是局部自动存储类型,拥有局部属性,拥有自动生命周期,所以在这里auto就没有啥体现价值。c++11了,就弃掉原来的用法,对其进行了翻新,将其用于实现自动类型推断,如此,定义时要求进行显示初始化,让编译器识别初始化值的类型,将定义对象的类型设置为初始化值的类型。
#include <iostream>
#include <vector>
#include <map>
using namespace std;
int main()
{int i = 3;auto p = &i;//已知&i类型,编译器根据其类型自动将变量p的类型推导为int*类型。auto pf = strcpy;//strcpy是一个函数,其也拥有返回值类型//为了能够验证他们的返回值类型,可以通过typeid,其作用获取类型名,以c-style字符串形式返回类型名//注意:对非引用类型,typeid().name()是在编译时期识别的,只有引用类型才会在运行时识别。cout << typeid(p).name() << endl;cout << typeid(pf).name() << endl;return 0;
}
输出结果:
3.2decltype
将变量的类型声明为表达式指定的类型,可用作模板实参,定义对象。
#include <iostream>
#include <vector>
using namespace std;
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;vector<decltype(y)> v{ 1.1,2.2,3.3,4.4 };//用作模板参数for (auto e : v){cout << e << " ";}return 0;
}
输出结果:
当然还有nullptr,在c++中,NULL是被当做0,而nullptr才被当做空指针((void*)0)。
end~