统一的初始化方法
-
通过花括号实现各类变量初始化(允许花括号嵌套)
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;
|