C++11新特性概述
C++11标准是C++语言的重大改动。在C++11之前,C++本身更像C语言,兼容和借用了C语言的很多特性。在C++11之后,C++语言更多是借鉴了JAVA、python等所谓现代语言的特点。经过C++11的标准,C++语言长得更像现代语言,但同时,也导致了C++语言的多种特性:既不像C语言本身那么精炼,又不像真正的纯面向对象语言。C++11之后的C++语言,功能更加复杂,更加多样,也造成了C++的编译器过重(几乎在所有语言中,C++编译器是运行最慢的)。
C++11增加了很多特性,我本人在以前的博客中也有过介绍。今天对这几个特性进行论述:1)std::stoi; 2)匿名函数;3)右值引用。
1.std::stoi等函数
std::stoi等函数均是C++11才引入的,一起引入的还有atoll、stoul、stoull、stof、stod、stold和to_string。stoi严格来说,最大的特点不是增加了标准库的字符串转为数字,而是引入了异常机制。
std::stoi的异常规则是:
1)若不能进行转换则为 std::invalid_argument;
2)若转换值会落在结果类型的范围外,或若底层函数( std::strtol 或 std::strtoll )设置 errno 为 ERANGE 则为 std::out_of_range 。
对于标准字符串为空串,std::stoi将抛出 std::invalid_argument异常。对于超出范围的数,则抛出std::out_of_range的异常。这和atoi函数是有区别的。atoi对于错误的数,会返回0而不抛出异常。
这个函数,其实隐含着几个思想:
1)C++11对异常的支持:在很多C++思想上,是对异常不支持的,认为异常带来了冗余,并无过多优点;但C++11更多的是对异常这一机制的支持。
2)错误处理机制:明确支持范围,对于不支持的语法,进行暴露而不是屏蔽。
2.匿名函数
匿名函数是C++11引入的。但严格来讲,C++很早就有类似匿名函数的思想,比如函数指针,比如仿函数。
匿名函数的思想是:不提供具体的函数命名,而只提供程序表达式;从逻辑上讲,这些程序表达式是一个功能,但不提供具体的名称;从实体上讲,这些部分作为一个内存块运行在内存中,而不像传统的函数一样,提供了一个地址作为变量名,通过地址指向运行内存。
匿名函数又成为Lamba表达式,语法是:[capture](parameters)->return-type{body},其中参数类型和返回类型可以省略。
一个简单的匿名函数的样例是:
[](int x, int y) -> int { int z = x + y; return z; }
匿名函数(Lambda函数)可以引用在它之外声明的变量。这些变量的集合叫做一个闭包。闭包被定义在Lambda表达式声明中的方括号[]内. 这个机制允许这些变量被按值或按引用捕获.下面这些例子就是:
[] //未定义变量.试图在Lambda内使用任何外部变量都是错误的.
[x, &y] //x 按值捕获, y 按引用捕获.
[&] //用到的任何外部变量都隐式按引用捕获
[=] //用到的任何外部变量都隐式按值捕获
[&, x] //x显式地按值捕获. 其它变量按引用捕获
[=, &z] //z按引用捕获. 其它变量按值捕获
简单提供一个匿名函数的例子:
#include <vector>
#include <iostream>
#include <algorithm>
int main(int argc, char *argv[])
{
std::vector<int> vTest;
int iTotal = 0;
for(int i = 0; i < 10; i++)
{
vTest.push_back(i);
}
std::for_each(
begin(vTest),
end(vTest),
[&iTotal](int x)
{
iTotal += x;
}
);
std::cout << iTotal << std::endl;
return 0;
}
匿名函数的语义含义是:提供一个程序块,可以自由实现功能,而不注重这个程序的位置和命名。
匿名函数的优缺点:
1)优点:函数(功能)随时实现和释放,灵活好用;
2)缺点:匿名函数一般功能比较单一,并作为独立功能支持是缺乏优势的。
匿名函数最先体现在Lisp语言中,现在被C++、JAVA、C#、python、Golang广泛支持。
3.右值引用
在计算机语言中,左值和右值具有很大不同。左值在寻址过程中是可识别的,而右值在内存识别中是不可识别的。这会造成语法的差异性:
int x;
x = 4; //支持
4 = x; //不支持
传统上,一直有对左值的引用,但C++11增加了对右值的引用;右值的引用,意味着可以对右值获得控制权,并对右值进行操作。
C++11右值的语法如下:
#include <iostream>
void process_value(int& i)
{
std::cout << "LValue processed: " << i << std::endl;
}
void process_value(int &&i)
{
std::cout << "RValue processed: " << i << std::endl;
i += 1;
std::cout << "RValue processed: " << i << std::endl;
}
int main(int argc, char *argv[])
{
int a = 0;
process_value(a);
process_value(1);
return 0;
}
右值引用是用来支持转移语义的。转移语义可以将资源 ( 堆,系统对象等 ) 从一个对象转移到另一个对象,这样能够减少不必要的临时对象的创建、拷贝以及销毁,能够大幅度提高 C++ 应用程序的性能。临时对象的维护 ( 创建和销毁 ) 对性能有严重影响。
转移语义是和拷贝语义相对的;拷贝语义意味着资源全部重新生成一遍,而转移语义只是让资源的所有权换到了另一个部分。
理解转移语义的一个重要参考是浅拷贝。但与浅拷贝不同的是:如果是浅拷贝的话,一个内存对象销毁,另一个浅拷贝的把内存也会销毁;但转移语义由于添加了引用,当一个内存对象销毁时,内存并不会释放掉。
这两段程序上传到github: