C++11简介
在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了 C++98称为C++11之前的最新C++标准名称。不过由于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++98
允许使用花括号{}对数组元素进行列表初始化:
int array1[3] = {1,2,3};
int array2[5] = {0};
但是,对于自定义类型,该方法失效:
std::vector<int> vec{1,2,3,4,5};
// C++98语法不支持这种初始化方式
C++11
C++11扩大了{}初始化列表的初始化范围,可用于所有的内置类型和用户自定义类型:
内置类型
int main()
{
// 内置类型变量
int x1 = {10};
int x2{10};
int x3 = 1+2;
int x4 = {1+2};
int x5{1+2};
// 数组
int arr1[5] {1,2,3,4,5};
int arr2[]{1,2,3,4,5};
// 动态数组,在C++98中不支持
int* arr3 = new int[5]{1,2,3,4,5};
// 标准容器
vector<int> v{1,2,3,4,5};
map<int, int> m{{1,1}, {2,2,},{3,3},{4,4}};
return 0;
}
列表初始化可以带上等号(=),也可以不带;
自定义类型
单个对象的列表初始化
同样,列表初始化也可以进行自定义类型的初始化:
#include <iostream>
class Example{
public:
Example(int a, int b)
:_a(a),
_b(b)
{}
void Print(){
std::cout << "_a = " << _a << std::endl << "_b = " << _b;
}
private:
int _a;
int _b;
};
int main(void){
Example exa1{1,2};
exa1.Print();
return 0;
}
根据结果,可以发现列表初始化完成了exa1对象的初始化。
多个对象的列表初始化
多个对象想要支持列表初始化,需给该类(模板类)添加一个带有initializer_list类型参数的构造函数即 可。
#include <iostream>
template<class T>
class Example{
public:
Example(std::initializer_list<T> list)
:_capacity(list.size()),
_size(0)
{
_array = new T[list.size()];
for(auto element : list){
_array[_size++] = element;
}
}
void Print(){
for(int i = 0; i < _capacity; ++i){
std::cout << _array[i] << ' ';
}
std::cout << std::endl;
}
private:
T* _array;
size_t _size;
size_t _capacity;
};
int main(void){
Example<int> exa1({1,2,3,4,5});
exa1.Print();
return 0;
}
变量类型推导
对于我们难以确定的变量类型,我们可以使用C++11新引入的 变量类型推导:
auto
当使用auto声明变量时,编译器会根据初始化表达式的类型推导出变量的类型;
#include <iostream>
int main(void) {
auto a = 10;
std::cout << typeid(a).name() << std::endl;
return 0;
}
显示的结果为int,说明auto关键字实现了变量类型的正确推导;
auto常见用于迭代器类型变量的推导:
#include <iostream>
#include <vector>
std::vector<int> vec(10, 1);
void Print_Vector(std::vector<int>& vec) {
for (auto it = vec.begin(); it != vec.end(); it++) {
std::cout << *it << ' ';
}
std::cout << std::endl;
for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); it++) {
std::cout << *it << ' ';
}
std::cout << std::endl;
}
int main(void) {
Print_Vector(vec);
return 0;
}
Print_Vector函数中的两种打印方式都是一样的,但是明显可见使用auto自动推导的方法更为便捷;
decltype
为什么要使用decltype,难道auto不能满足所有情况吗?
确实,上面的例子其实都是初始化阶段,可见auto的一大弊端就是只能用在类型初始化时的推导,当遇到执行过程中的类型推导,就需要使用decltype;
#include <iostream>
int main(void) {
int a = 10;
double b = 20;
decltype (a + b) c;
std::cout << typeid(c).name() << std::endl;
return 0;
}
打印结果是double,可见decltype是在a+b执行之后才进行的类型推导:
此外,decltype还可以对函数返回参数的类型进行推导:
#include <iostream>
template<class T>
T Func1(T a = 10) {
int b = 2;
return a + b;
}
int main(void) {
std::cout << typeid(decltype(Func1(10))).name() << std::endl;
std::cout << typeid(decltype(Func1(10.1))).name() << std::endl;
}
打印结果分别为int\double;
范围for循环
范围for循环是C++11引入的一个方便的语法结构,用于遍历容器中的元素。它使得代码更简洁易读,尤其适用于遍历容器或数组等序列型数据结构。
注意,能够支持范围for循环的结构必须支持迭代器的存在,因为范围for底层是通过迭代器实现的;
#include <iostream>
#include <vector>
std::vector<int> vec(10, 1);
int main(void) {
for (auto element : vec) {
std::cout << element << ' ';
}
std::cout << std::endl;
return 0;
}