第十章 C++11新特性
统一的初始化方法
-
通过花括号实现各类变量初始化(允许花括号嵌套)
int arr[3]{1,2,3}; vector<int> iv{1,2,3}; map<int,string> mp{{1,"hello"},{2,"world"}}; int *p=new int[20]{1,2,3}; //前三个变量初始化为1,2,3,其余用默认构造函数初始化 struct a{int i,j; a(int m,int n):i(m),j(n){}} a func(int m,int n){return {m,n};} a* pa=new a{2,3};
成员变量可以赋予默认初始值
- 示例
class a{ public: i=5; }
auto自动类型关键字
-
定义变量时,通过所赋予的值在编译时自动判断变量类型
-
可用于模板返回值,提升模板灵活性
template<class t1,class t2> auto func(t1 x,t2 y)->decltype(x+y){ return x+y; } //auto可以不为t1、t2中的一种,->decltype(x+y)返回值类型声明也可省略
decltype关键字
-
用于获取变量或表达式返回值类型
-
双括号返回相应类型的引用
int i; decltype(i) x=5; decltype((i+x)) y=x;
基于范围的for循环
-
格式:
for(元素类型 i:数组等名)
-
获取时元素,而非指针
int ary[]{1,2,3,4,5}; for(int &e:ary) e*=2; for(int e:ary) cout<<e<<endl; //2,4,6,8,10 map<int,string> mp{{1,"a"},{2,"b"}}; for(auto &e:mp) cout<<e.first<<" "<<e.second<<endl;
智能指针shared_ptr
-
需要头文件
<memory>
-
写法:shared_ptr
ptr(new t); -
自动托管指针,程序结束或托管数为0时自动释放new动态分配的内存
-
多个shared_ptr对象可以同时托管一个指针,系统会维护一个托管计数
-
不能用于托管指向动态分配的数组
-
创建后的指针用法与一般指针相同
int *p=new int,*p2=new int{1}; shared_ptr<int> sp1(p); shared_ptr<int> sp2(sp1); sp2.reset(); //清空sp2 sp2.reset(p2); //会置托管计数为1,只能用于智能指针第一次指向p2区域。否则可能出错(delete多次) sp2=sp1; p2=sp1.get(); //get函数获取地址
空指针nullptr
-
与null和0的区别:包含类型,能够转换位bool型,却不能转化为整型
-
可以直接和null和nullptr比较
-
不同类型的指针不能比较(如double*和int* 型)
int *p1==null,*p2=nullptr; shared_ptr<double> p3=nullptr; if(p1==p2) cout<<"equal\n"; //相等 if(p3==null) cout<<"equal\n"; //相等 if(p3==nullptr) cout<<"equal\n"; //相等 bool b=nullptr; //b=false int i=nullptr; //错误❌
无序容器(哈希表)
unordered_map
- 使用与map类似
- 插入、查找元素的时间复杂度几乎为常数,但空间占用较多
lambda表达式
-
[外部变量范围方式说明符](形参表)->返回值类型{ 语句组 }
"->返回值类型"也可以没有,由编译器自动判断外部变量访问符 含义 [] 不使用任何外部变量 [=] 以传值方式使用外部变量 [&] 以引用方式使用外部变量 [x,&y] 传值方式使用x,引用方式使用y [=,&x,&y] 引用方式使用x,y,传值方式使用其他变量 [&,x,y] 传值方式使用x和y,引用方式使用其他变量 -
示例
cout<<[](double x,double y){return x+y;}(1.1,2.2)<<endl; //3.3 int x=100,y=200; auto ff=[&x,&y](int n){ x++;y++; return n*n; } cout<<ff(15)<<" "<<x<<" "<<y<<endl; //225 101 201 function<int(int)> fib=[&fib](int n){return n<=2?1:fib(n-1)+fib(n-2);} //无法由返回值确定返回类型,不能用auto。第一个int代表返回值,第二个int代表函数形参表 cout<<fib(5)<<endl; sort(arr,arr+5,[](int a,int b){return a<b;});
右值引用t&&和move移动语义
-
详见
-
c++
中所有的值都必然属于左值、右值二者之一。左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象。所有的具名变量或者对象都是左值,而右值不具名。很难得到左值和右值的真正定义,但是有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值。 -
目的:提高程序运行效率,减少需要深拷贝的对象的深拷贝次数(通过直接夺取变量已分配动态分配空间)
-
具体类型:
左值引用, 使用t&
, 只能绑定左值右值引用, 使用
t&&
, 只能绑定右值常量左值, 使用
const t&
, 既可以绑定左值又可以绑定右值已命名的右值引用,编译器会认为是个左值
编译器有返回值优化,但不要过于依赖
-
移动构造函数 和移动赋值函数,如
string(string&& str)
和string& operator= (string&& str)
-
std::move()
用于告诉编译器尽量将该变量当成右值处理,不存在移动拷贝、移动赋值时再考虑普通方式 -
dev c++中,return局部对象,对导致优化,不调用移动或复制构造函数
-
可移动但不可复制的对象:
struct a{ a(const a& a)=delete; a(const a&& a){cout<<"move constructor."<<endl;} a(){} }
正则表达式
-
需包含
<regex>
头文件 -
示例:
regex reg("\\d{3}{[a-za-z]+}.(\\d{3}|n/a)\\s\\1"); //在c/c++中反斜杠必须写2个以反义 cout<<regex_match("123hello n/a hello",reg)<<endl; //输出1,匹配成功 string s("123hello n/a hello"); cout<<regex_match(s,reg)<<endl; //输出0
强制类型转换
- static_cast
- 用于比较“自然”和低风险类型的转换,如整型与字符型、浮点型的转换
- 不能用于不同类型指针、引用转换,或整型与指针的转换
- reinterpret_cast
- 用于不同类型指针、引用的转换,以及指针和能容纳下指针的整数类型的转换,执行逐个比特拷贝的操作
- 不进行类型检查
- const_cast
- 用来去除const属性,将const指针、引用转为同类型非const类型引用
- dynamic_cast
- 专门用于将多态基类指针或引用转换成派生类指针、引用。
- 对于不安全的指针转换(指针不是指向派生类对象),转换结果等于null
- 对于不安全的引用转换,抛出bad_cast异常
异常处理
-
在
try
中进行可能异常的操作,将异常传递给catch
-
throw
主动抛出异常 -
catch
捕获try传递的异常,可以存在多个catch函数,从上到下对异常进行类型匹配,catch(...)
表示匹配任意类型 -
发生异常后,try范围内后续命令不再执行,直到异常被捕获
-
如果异常未在函数内部处理,就会被抛给上一级的函数
-
常见的异常类(从exception类派生而来,位于
<stdexcept>
中,类型异常位<typeinfo>
中-
out_of_range
如vector、string用at函数范围越界时 -
bad_alloc
动态分配内存请求失败(typeinfo头文件中) -
bad_cast
用dynamic转换基类引用失败(typeinfo头文件中)
--> 通过异常对象的what()函数获取异常信息
-
运行时类型检查
-
需要
<typeinfo>
头文件 -
对于多态类,会返回实际指向的对象类型
-
示例
long n; cout<<typeid(n).name()<<endl;