C++11是自C++98十余年来发布的一个新特性,扩充了很多C++的功能和特性,而C++14是对C++11的又一次补充和优化,这些新特性使得C++更贴近于一种现代化的变成语言。gcc版本大于5(clang版本大于3.8)已经全面支持C++14。
1.Lambda 表达式
Lambda表达式,可以方便的定义和创建匿名函数。
Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
各项具体含义如下
- capture list:捕获外部变量列表
- params list:形参列表
- mutable指示符:用来说用是否可以修改捕获的变量
- exception:异常设定
- return type:返回类型
- function body:函数体
“不完整”的Lambda表达式 :
[capture list] (params list) -> return type {function body}
[capture list] (params list) {function body}
[capture list] {function body}
示例:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
bool cmp(int a, int b)
{
return a < b;
}
int main()
{
vector<int> myvec{ 3, 2, 5, 7, 3, 2 };
vector<int> lbvec(myvec);
sort(myvec.begin(), myvec.end(), cmp); // 旧式做法
cout << "predicate function:" << endl;
for (int it : myvec)
cout << it << ' ';
cout << endl;
sort(lbvec.begin(), lbvec.end(), [](int a, int b) -> bool { return a < b; }); // Lambda表达式
cout << "lambda expression:" << endl;
for (int it : lbvec)
cout << it << ' ';
}
2.类型推导和判断
C++11中引入auto第一种作用是为了自动类型推导。auto的自动类型推导,用于从初始化表达式中推断出变量的数据类型。通过auto的自动类型推导,可以大大简化我们的编程工作。
auto实际上实在编译时对变量进行了类型推导,所以不会对程序的运行效率造成不良影响
另外,似乎auto并不会影响编译速度,因为编译时本来也要右侧推导然后判断与左侧是否匹配。
auto i = 1; //OK
auto i; //ERR,auto需要一个初始化值完成推导
int max(int, int)
auto fun = max; //OK
auto str = "ABC"; //OK
int max(auto a, auto b); //ERR,因为重载的原因,不能这么使用
for (auto iter = vec.cbegin(); iter != vec.cend(); iter++){}; //OK
template <typename T1, typename T2, typename T3>
auto add(T2 a, T3 b) { //仅在C++14中合法,c++11不支持
return a+b;
}
类型判断的引入主要是为了获取变量的类型,使用decltype()
可以在编译期间获取变量的类型:
auto a = 1;
auto b = 2;
decltype(a+b) c; //ok
3.nullptr
和constexpr
引入nullptr
是为了解决NULL
的诟病,在之前的c++中,NULL
可以被定义为int
0或者一个0值的指针,这就带来一个问题:
void fun(int);
void fun(void *);
fun(NULL); //无法确定使用的是哪一个重载函数,需要视NULL的定义而定
而nullptr
现在定义为一个空指针,避免了NULL带来的问题。
constexpr
定义了一个用户显式的声明函数或对象构造函数在编译期间会成为常数,从 C++14 开始,constexptr
函数可以在内部使用局部变量、循环和分支等简单语句:
//这个函数会在编译期间进行计算
constexpr int fun(const int n) {
if (1 == n) return 1;
else if (2 == n) return 1;
else return fun(n - 1) + fun(n - 2);
}
4.序列for循环
C++11引入了一种简单的for语法用于快速迭代序列:
std::vector<int> a = {1, 2, 3, 4};
for (auto item : a) {
std::cout << item << std::endl;
}
5.初始化列表扩展
在引入C++11之前,只有数组能使用初始化列表,其他容器想要使用初始化列表,只能用以下方法:
int arr[3] = {1, 2, 3}
vector<int> v(arr, arr + 3);
在C++11中,我们可以使用以下语法来进行替换:
int arr[3]{1, 2, 3};
vector<int> iv{1, 2, 3};
map<int, string>{{1, "a"}, {2, "b"}};
string str{"Hello World"};
6.类中默认函数行为
我们知道在没有指定的情况下,c++会对类设置默认的构造函数、拷贝构造函数、赋值函数以及析构函数,但是有时候我们并不需要这些默认函数,因此在C++11中引入了对这些特性进行精确控制的特性:default指定生成默认函数,delete指定禁用默认函数。如果禁用了默认的构造函数和析构函数,必须指定一个自定义的函数。
class Test {
public:
Test() = default; //指定为Test类生成默认构造函数,如果设置为delete,就是禁用默认构造函数,如果禁用了
~Test() = default; //默认析构函数
Test(const Test&) = delete; //禁用拷贝构造函数
Test& operator=(const Test&) = delete; //禁用类赋值函数
};
Test a;
Test b(a); //error,因为已经被禁用
Test c = a; //error,因为已经被禁用
7.显式控制虚函数重载
由于虚函数的特性,可能会被意外进行重写,为了做到精确对虚函数重载的控制,c++11使用了override
和final
关键字完成对这一特性的实现,下面看例子:
class Test {
public:
Test() {};
virtual int fun(int);
};
class Test2 : public Test {
using Test::Test;
int fun(int) override; //显式声明对虚函数进行重载
int fun(float) override; //错误,父类没有这个虚函数
}
而final
关键字是为了显式终结类的继承和虚函数的重载使用:
class Test {
public:
virtual int fun(int) final;
};
class Test2 final: public Test {
int fun(int) override; //非法,因为该虚函数已经设置为finale,禁止重载
};
class Test3 : public Test2 {}; //非法,Test2已经设置为final,禁止作为父类
8.模板增强
类型别名:使用using
关键字更加直观的定义别名:
typedef int (*fun)(int *); //以前c++的做法,声明一个参数为`int *`,返回值为int的函数指针,名字叫fun
using fun = int (*)(int *); //c++11,这样更加直观
template <typename T>
using newType = std::pair<T, T>;
变长模板和默认模板参数:
c++11可以在定义模板时,给与模板一个默认的类型,这样可以在不设置类型的时候使用默认类型:
template <typename T = int, typename U = std::string>
同时c++11可以设置模板参数为任意个数:
template <typename T1, typename... TS> //可以接受至少一个模板类型
template <typename... TS> //至少0个
9.标准库扩充
c++11对多个标准库进行了扩充,包括:
1. 新增加容器
- 数组容器:std::array
- 单项链表容器:std::forward_list
- 无序容器:std::unordered_set
- 无序映射:std::unordered_map
- 元组:std::tuple
2. 正则表达式
3. 语言级的线程支持
4. 智能指针和引用计数
5. 函数绑定和包装