引用
左值右值判断
int x = 1; x左值 1右值
int y = 3; 同上
int z = x + y 同上
特殊情况:
int a = 1;
int b = a; 此时a为左值
c++的左值指的是有一稳定内存的值或对象,该值或对象拥有稳定且较长的生命周期
a已经被赋值,占有一段稳定内存且长期存在,所以他是左值
c++的右值指的是临时对象,如函数返回,其生命周期较短
我们可以用取地址符来判断左值还是右值,能取到地址的是左值,取不到的是右值
如,取地址a &a,程序合法,a是左值。但取地址1 &1,程序不合法,1不是右值
一些例子:
int x = 1;
int get_val()
{
return x;
}
void set_val(int val)
{
x = val;
}
x++; 右值, 取地址不合法
++x; 左值,取地址合法
int y = get_val(); y是左值,get_val是右值,返回的x是临时变量
set_val(5); 5传入的时候是右值,传入后在函数中val成了左值
所有的字面量除了字符串外都是右值,字符串在使用时自行开辟空间
左值引用 type &
看等号右边的值是不是左值
int & x1 = 7; 程序报错,左值引用无法绑定右值
原因左值引用x1绑定右值7,只有等号右边的值是左值才能通过
const int & x1 = 7 常量引用:左值是一个常量可以使左值引用绑定到右值上
右值引用 type &&
int && x1 = 7
可以延长右值的生命周期
int i = 0;
int && x1 = i; 程序报错,右值引用无法绑定左值
原因右值引用&&左值x1,绑定到左值i,只有等号右边的值是右值才能通过
左值引用只能引用左值绑定,右值引用只能引用右值绑定
解决上述问题
int && x1 = static_cast<int &&>(i) 强制将左值转换为右值,将i左值,转换成右值,也叫将亡值
万能引用
模板中使用&&
template<tempname T>
T foo(T && i ){}
auto res = foo(123)
在使用该模板时,会在初始化时类型推导,在推导过程中,若初始化的对象时左值,就会推导出左值引用,右值同理。
转发函数
template<typename T>
void show_type(T tValue)
{
std::cout << typeid(tValue).name() << std::endl;
}
template<typename T>
void show_type_forwarding(T tValue)
{
show_type(tValue);
}
上述函数是一个转发函数,效率低下,在tValue前加上&可解决,此时为左值引用
如果要传右值,加上const,&&并且在转发的函数将参数改为static_cast<T&&>(tValue)
即
void show_type_forwarding(T && tValue)
{
show_type(static_cast<T&&>(tValue));
}
std::string szString = "Hello";
show_type_forwarding(szString);
移动表达式
int x = 5;
auto func = [x](int y)->int{return x * y; };
[captures]捕获当前作用域下的列表 (Params)参数 形参
->int 返回类型 函数返回类型后置 {code} 代码块
autuo func 相当于函数指针的对象
std::cout << func(8) << std::endl;
打印结果40 5 * 8 = 40
泛型的移动语句
auto func2 = [](auto nValue){return Value};
std::cout << func2("hello") << func2(123) << std::endl;
打印hello123
异常
int x = 0;
int z = 5 / x; 程序报错 被零除或对零求模
处理异常
如果该异常可以猜测
#include<Window.h>
Windows异常:
_try
{
int z = 5 / x;
}
_except(EXCEPTION_EXECUTE_HANDLER)
{
std::cout << "zreo" << std::endl; 将可能发生异常的错误打印出去
}
void test() noexcept 告诉编译器该函数没有异常
{
throw 123; 函数体中再抛出一个异常,编译器会报错
}
现代if switch语句
if语句
bool getflag()
{
return true;
}
if(bool b = getflag(); b) 带初始化的if语句,将b初始化
{
std::cout << b << std::endl;
}
switch语句
switch(bool b = getflag(); b)
{
case 0:
std::cout << 0 << std::endl;
break;
case 1:
std::cout << 1 << std::endl;
break;
default:
break
断言
包含头文件 <cassert>
断言关键字assert
断言分为运行时断言和编译器断言(静态断言),用于安全检查,括号中代码是否成立
运行时断言
char* szBuffer = nullptr;
assert(szBuffer == nullptr);
如果括号中语句符合表达式情况,程序可正常运行
如果不正确,程序不可运行
编译器断言
定义一个宏
#define FLAG 0;
static_assert(FLAG == 0); 不报错
static_assert(FLAG != 0); 报错,编译器都过不去
此时在后面加上"error:FLAG != 0",括号中表达式不符时,可以显示自己编写的错误提示
元组
包含头文件 <tuple>
元组可以返回多个不同类型
主函数外
std::tuple<int, double, std::string> getreturn()
{
return std::male_tuple(12, 7.5, "hello")
}
主函数中
auto[x, y, z] = getreturn(); 利用auto接收
std::cout << x << std::endl;
std::cout << y << std::endl;
std::cout << z << std::endl;
此时可以正常打印 12 7.5 hello
结构体中使用:
struct
{
int nNum = 15;
std::string szStr = "Hello"
}obj;
auto[a, b] = obj;
std::cout << a << std::endl
std::cout << b << std::endl
打印15 Hello
别名
函数取别名
void print(int nValue)
{
std::cout << nValue << std::endl;
}
typedef void(*fnptr)(int);
C++新式起别名
using fn = void(*)(int);
fn a = print;
a(12); 给a传值
模板取别名
需要使用容器,包含头文件map
此时一个模板
template<typename T)
using int_map = std::map(int, T)
map标准作用域下的类模板,上式将std::map(int, T)声明为int_map
主函数应用:
int_map<std::string> ins; 相当于简写原模板名了,不用写复杂的std::map(int, T)