C++11标准由国际标准化组织(ISO)和国际电工委员会(IEC)旗下的C++标准委员会(ISO/IEC JTC1/SC22/WG21)于2011年8月12日公布。此次标准为C++98发布后13年来第一次重大修正。
那么C++11有哪些重要的修改呢?
1.字符串的原始字面量
使用R"ABC(raw string)ABC"的格式来解决如路径地址,换行等字符转义问题,其中ABC签后必须一样
如原本需要写成"D:\AAA\BB\ccc.txt)“的路径,可以使用R来代替:R”(D:\AAA\BB\ccc.txt)"
一行代码分成多行,用""连接,或者用"R()"给包起来
如果代码中的字符本身就表示他的原始含义,则不需要使用R转义符,只有当字符不能表示原始含义时需要R来修饰
2.空指针字符nullptr
用nullptr替换NULL,因为NULL在C语言中被定义为((void *)0),而在C++中,直接被定义为0,因为C++中不允许(void )转换为其他类型的,这就造成fun(int n)和fun(char p)无法区分的情况
nullptr是为C++量身定制的,可以自动隐式转换为对应的指针类型
3.constexpr修饰常量表达式
原理
原来的const有两种语义:定义只读变量和修饰常量,如:定义函数fun(const int num){const int nCount = 10; const int nRet = nCount+5; },其中形参num是制度变量不是常量,而函数内部的nCount和nRet则是常量,不是只读变量
程序编写到执行分为:预处理,编译,汇编,链接4个阶段,得到可执行程序才能运行,普通表达式是在运行阶段计算结果,而常量表达式是在编译阶段计算结果,这样极大的提高了程序运行效率
修饰变量
C++11中增加constexpr来修饰常量表达式,而const只用来修饰只读属性;所以上面函数改为:fun(const int num){constexpr int nCount = 10;constexpr int nRet = nCount+5; },这样修改后,nCount =10;和nCount+5就被认为式常量表达式,就会在编译期间计算出结果
修饰类对象
constexpr不能直接修饰struct结构体和class类,只能在struct或class创建对象时,用constexpr修饰为常量,并进行初始化。
例:struct T{int num;} fun(){constexpr T obj{20}; obj.num=5;},这时候obj.num=5是非法的,因为T中的num是常量了,不能被修改了
修饰函数
用constexpr修饰函数,被称为常量表达式函数,可修饰:普通函数/类成员函数,类构造函数,模板函数
constexpr修饰的函数,constexpr写在最前面,且函数必须要有返回值,并且return返回的表达式必须是常量表达式
例:constexpr int fun(){ constexpr int a=15; return a;},函数实现中,constexpr修饰函数,函数有返回值,返回值a是常量
constexpr修饰的常量表达式函数,必须要有函数的声明和定义
整个常量表达式函数的函数体中,不能出现非常量表达式之外的语句(using指令,typedef语句,static_assert断言,return语句除外)
constexpr可以修饰模板函数,但由于模板类型的不确定性,因此模板函数实例化后,函数是否符合常量表达式的函数要求也是不确定的,如果constexpr修饰的模板函数实例化结果不满足常量表达式的函数要求,则constexpr会被自动忽略
4.初始化列表
C11中统一了用{}大括号对任意类型的变量进行初始化
使用方式:
int a1 = {25};
int a2{30};
Animal animal{25,“zhangsan”};
Animal* animal = new Animal{28,“lucy”}
注意点:
1.类中有私有成员,不能使用初始化列表进行初始化;
2.类中有静态成员变量,不能对静态成员变量进行初始化;
3.类中不能有自定义的构造;
4.类不能有其父类;
5.类中不能有虚函数;
5.std::initializer_list使用
C11中提供了std::initializer_list可以让类模板实现接收任意长度的的同种类型的初始化列表
6.智能指针
智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加1,每析构一次内部的引用计数减1,减为0时,删除所指向的堆内存,解决C++中的内存回收问题
std::shared_ptr 共享智能指针
std::unique_ptr 独占智能指针
std::weak_ptr 弱引用智能指针
7.非范围的for循环
不以开头和结尾进行遍历,而是以容器中的元素进行枚举;并不能代替传统for循环,二者之间各有优缺点
语法:
vector<string> vctInfo;
//传统方式
for(auto it = vctInfo.begin(); it != vctInfo.end(); ++it)
{cout << it << endl; }
//非范围方式
for(auto& it : vctInfo)
{cout << it << endl; }
int array art{1,3,5,7,9};
for(int n : art)
{ cout << n << endl; }
8.Lambda表达式
优点:就地匿名函数定义或函数对象,避免代码膨胀,在需要时,实现功能闭包,声明式编码风格,多用于有特定功能的,不重复使用的函数
语法:capture opt -> ret {body;};,其中capture是捕获列表,params是参数列表,opt是函数选项,ret是返回值类型,body是函数体
捕获列表可以捕获一定范围内的变量,具体使用方式如下:
[] - 不捕捉任何变量
[&] - 捕获外部作用域中所有变量, 并作为引用在函数体内使用 (按引用捕获)
[=] - 捕获外部作用域中所有变量, 并作为副本在函数体内使用 (按值捕获)
拷贝的副本在匿名函数体内部是只读的
[=, &foo] - 按值捕获外部作用域中所有变量, 并按照引用捕获外部变量 foo
[bar] - 按值捕获 bar 变量, 同时不捕获其他变量
[&bar] - 按引用捕获 bar 变量, 同时不捕获其他变量
[this] - 捕获当前类中的this指针
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
实现原理:1.内部先创建 lambda匿名类,实现构造函数,使用Lambda表达式的函数体重载operator()(也叫仿函数),然后创建对象,,再通过对象调用operator(),所以Lambda表达式本质上时一个函数对象
9.右值引用
使用"&&"来进行右值引用;用于实现移动语义和完美转发
右值引用可以绑定到临时对象(右值),通过将资源的所有权从一个对象转移到另一个对象,避免了不必要的复制和销毁操作,提高程序效率
右值引用还可以用于函数模板的完美转发,即将参数以原始的形式传递给下一个函数,避免了不必要的复制和类型转换,提高了程序效率
右值引用类型可能是左值也可能是右值
编译器会将已命名的右值引用视为左值,将未命名的右值引用视为右值
通过右值推导 T&& 或者 auto&& 得到的是一个右值引用类型,其余都是左值引用类型
auto&&或者函数参数类型自动推导的T&&是一个未定的引用类型,它可能是左值引用也可能是右值引用类型,这取决于初始化的值类型
10.转移和完美转发
std::forward()完美转发函数,右值引用类型是独立于值的,一个右值引用作为函数参数的形参时,在函数内部转发该参数给内部其他函数时,它就变成一个左值,并不是原来的类型了。如果需要按照参数原来的类型转发到另一个函数,需可以用std::forward()完美转发
std::move()函数实现转喻,将佐治转换为右值;使用move几乎没有不需要拷贝过程,只是转换了资源的所有权。使用方法:
Test t;
Test && v2 = move(t);
将上面的Test类对象t从左值转移为右值