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;
}