C++11新特性
C+11的标准规范于2011年2月正式落稿,而此前10余年间,C++正式标准一直是C++98/03。相比C++98/03,C++11有了非常多的变化。
包括大量的新特性:主要特征像lambda表达式和移动语义,实用的类型推导关键字auto,更简单的容器遍历方法,和大量使模板更容易使用的改进。
C++11将修复大量缺陷和降低代码拖沓,比如lambda表达式的支持将使代码更简洁。像移动语义这种特性会提高语言内核的基础效率,使你可以写出更快的代码。对模板系统的优化可以使你更容易写出泛型的代码。
新的标准库同时也会包含新的特性,包括对模板”>>“写法的支持,对多线程的支持,优化智能指针,将给那些还没用类似于boost::shared_ptr的人提供更简单的内存管理方法。
你可以通过阅读维基百科里的C++11页面学习。
以下是
C++11常用特性的使用经验总结
1. nullptr
nullptr 出现的目的是为了替代 NULL。
在某种意义上来说,传统 C++ 会把 NULL、0 视为同一种东西,这取决于编译器如何定义 NULL,有些编译器会将 NULL 定义为 ((void*)0),有些则会直接将其定义为 0。
C++ 不允许直接将 void * 隐式转换到其他类型,但如果 NULL 被定义为 ((void*)0),那么当编译char *ch = NULL;时,NULL 只好被定义为 0。
而这依然会产生问题,将导致了 C++ 中重载特性会发生混乱,例如:
void TestWork(int index){} void TestWork(int * index){}
为了解决这个问题,C++11 引入了 nullptr 关键字,专门用来区分空指针、0。
当需要使用 NULL 时候,养成直接使用 nullptr的习惯。
2. 类型推导
C++11 引入了 auto 和 decltype 这两个关键字实现了类型推导,让编译器来操心变量的类型。
auto并没有让C++成为弱类型语言,也没有弱化变量什么,只是使用auto的时候,编译器根据上下文情况,确定auto变量的真正类型。
auto index = 10; //int auto str = "abc"; //char* auto arr = new auto(10) //int * auto ret = AddTest(1,2); //int
auto不能做什么?
auto 不能用于函数传参,因此下面的做法是无法通过编译的(考虑重载的问题,我们应该使用模板)。
auto 还不能用于推导数组类型。
decltype(Declared Type)“声明类型”
decltype 关键字是为了解决 auto 关键字只能对变量进行类型推导的缺陷而出现的。它的用法和 sizeof 很相似:
auto x = 1; auto y = 2; decltype(x+y) z;
在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
有时候,我们可能需要计算某个表达式的类型。
3. 基于范围的 for 循环
int numbers[] = { 1,2,3,4,5 }; for (auto number : numbers) { std::cout << number << std::endl; }
4. 初始化列表
C++11 提供了统一的语法来初始化任意的对象
A a {1, 1.1}; // 统一的初始化语法 B b {2, 2.2};
C++11 还把初始化列表的概念绑定到了类型上,并将其称之为 std::initializer_list,允许构造函数或其他函数像参数一样使用初始化列表,这就为类对象的初始化与普通数组和 POD 的初始化方法提供了统一的桥梁,例如:
#include <initializer_list> class Magic { public: Magic(std::initializer_list<int> list) {} }; Magic magic = {1,2,3,4,5}; std::vector<int> v = {1, 2, 3, 4};
5. 模板增强
在传统 C++ 的编译器中,>>一律被当做右移运算符来进行处理。但实际上我们很容易就写出了嵌套模板的代码:
std::vector<std::vector<int>> wow;
这在传统C++编译器下是不能够被编译的,而 C++11 开始,连续的右尖括号将变得合法,并且能够顺利通过编译。
6. Lambda 表达式
lambda 表达式能够方便地构造匿名函数,假设你的代码里面存在大量的小函数,而这些函数一般仅仅被调用一次。那么最好还是将他们重构成 lambda 表达式.
C++11 的 lambda 表达式规范例如以下:
[ capture ] ( params ) mutableexceptionattribute -> ret { body } |
(1) | |
[ capture ] ( params ) -> ret { body } |
(2) | |
[ capture ] ( params ) { body } |
(3) | |
[ capture ] { body } |
(4) |
当中
- (1) 是完整的 lambda 表达式形式。
- (2) const 类型的 lambda 表达式,该类型的表达式不能改捕获("capture")列表中的值。
- (3)省略了返回值类型的 lambda 表达式。可是该 lambda 表达式的返回类型能够依照下列规则推演出来:
- 假设 lambda 代码块中包括了 return 语句,则该 lambda 表达式的返回类型由 return 语句的返回类型确定。
- 假设没有 return 语句。则类似 void f(...) 函数。
- (4)省略了參数列表,类似于无參函数 f()。
mutable 修饰符说明 lambda 表达式体内的代码能够改动被捕获的变量。而且能够訪问被捕获对象的 non-const 方法。
exception 说明 lambda 表达式是否抛出异常(noexcept
)。以及抛出何种异常,类似于void f()throw(X, Y)。
attribute 用来声明属性。
另外,capture 指定了在可见域范围内 lambda 表达式的代码内可见得外部变量的列表。详细解释例如以下:
[a,&b]
a变量以值的方式呗捕获,b以引用的方式被捕获。[this]
以值的方式捕获 this 指针。[&]
以引用的方式捕获全部的外部自己主动变量。[=]
以值的方式捕获全部的外部自己主动变量。[]
不捕获外部的不论什么变量。
此外,params 指定 lambda 表达式的參数。
int main() { Test test; test.Add(add, 1, 2); TestAdd testAdd; test.Add(std::bind(&TestAdd::Add, testAdd, std::placeholders::_1, std::placeholders::_2), 1, 2); test.Add([](int a, int b)->int { std::cout << "lamda add fun" << std::endl; return a + b; },1,2); return 0; }
联想仿函数
7. 新增容器
std::array
std::forward_list
C++11 引入了两组无序容器:
std::unordered_map/std::unordered_multimap 和 std::unordered_set/std::unordered_multiset。
8. 语言级线程支持
std::thread
std::atomic
std::mutex/std::unique_lock
std::future/std::packaged_task
std::condition_variable
9. 智能指针内存管理
std::shared_ptr
std::weak_ptr
10. 右值引用和move语义
11.正则表达式
C++11 提供的正则表达式库操作 std::string 对象,对模式 std::regex (本质是 std::basic_regex)进行初始化,通过 std::regex_match 进行匹配,从而产生 std::smatch (本质是 std::match_results 对象)。
参考: