第四章: 表达式
基本概念:
运算符: 一元 , 二元 , 三元
组合运算符 和 运算对象 :
优先级: 使用 () 避免优先级的混淆的问题
结合律:
求值顺序: 二元运算符两边的表达式计算顺序可能存在差异 , 应该避免
对优先级 , 结合率 , 求值顺序的解释:
//如下表达式: //我们假设 g() 的操作是将全局变量 i = 10 的值 乘以 2 返回 //我们假设 h() 的操作是将全剧变量 i 的值 + 2 返回 f() + g() * h() + j() // 优先级 : 存在于组合运算当中 , 也就是多个运算符参与 , 优先级是运算符 与 运算符之间的关系 // 结合率 : 我们针对 上述表达式 可以 使用 结合率 的方式改变运算符的优先级 // ( f() + g() ) * h() + j() // 求值顺序 : 对于 单个二元运算符来说 , 先算右边 还是 先算左边 好像只有在 逻辑运算符 : || && 上才有体现 , 但是在 * 上可能存在差异 , 如上 , // 如果我们先算左边 , 在算右边 ; 那 g() * h() 的结果就是 20 * 22 = 440 // 如果我们先算右边 , 在算左边 ; 那 g() * h() 的结果就是 24 *12 = 288 // 所以我觉得 能不在一个运算符两边修改 共有的部分 是最好的。
运算对象转换 :
表达式中匀允许不同类型之间的转换 , 编译器会自动的转换
int 还会有类型提升
在算术类型之间的运算 需要保证 运算结果不溢出 , 且精度损失降低到最小 , 才有运算结果的类型一定是其中的最高类型。
重载运算符:
将运算符 赋予 在某一个非基本类型上面新的含义
左值 与 右值 :
将一个对象当作左值来使用 : 代表的是当前对象本身
将一个对象当作右值来使用 : 代表的是当前对象的拷贝
需要右值的地方可以使用 左值代替 , 但是反过来通常不行
decltype 与 左值 右值:
如果 decltype 的是一个左值类型 , 那么得到一定是一个引用类型 , 如果decltype 的是一个右值类型 , 那么得到的一定是一个 普通类型
#include <iostream> using namespace std; int main() { int a = 10; //普通的类新 int int & b = a; //引用类型 int & decltype(b) c = a; //c 的类型为 int & c = 20; //修改C 就是 修改a cout << a << endl; //结果为20 return 0; } ~
算数运算符:
略
逻辑运算符:
注意 : && || 有运算符求值顺序的规定 , 先算左边在算右边 , 左边满足的顺序右边不再计算 , 这就是短路
赋值运算符:
-
- = 左边必须是一个可以修改的左值
- C++11 允许使用 {} 扩起来的初始值列表做右值
- 连续赋值运算 满足 右边结合率 : a = b = 10
- 赋值运算符优先级低 : if ( 1 != (i = get_value())
递增运算符 和 递减运算符:
- ++ , -- 运算符 需要左值对象 : (i++)++ 错误 , i++返回的是右值
成员运算符:
. 与 ->
条件运算符:
> , < , >= ......
位运算:
& , | , ~ , ^
sizeof 运算符:
sizeof(类型) 可以获得该类型占用的内存大小 , C++ 可以让sizeof 直接访问 类的成员的 , 以此的其成员类型占用的内存的大小. sizeof (class::number)
- sizeof(数组) : 数组不会退化成指针)
- sizeof(类类型) : 固有成员的内存和 : 不包括成员指向的 堆空间的内存
- sizeof 返回的类型是一个 constexpr
逗号运算符:
结合律从左边向右边 , 最后的结果为最右边的表达式子 , 且如果最右边的表达式是左值 , 整个表达式结果为左值 , 否则为右值
类型转换:
如果两个类型之间可以相互转换 , 那么他们就是有关联的.
无须程序员的介入的类型转换称为隐式类型转换.
显式类型转换:
static_cast<>():
不能擦除引用 , 指针 的 底层 const 属性。
大范围类型 向 小范围类型 , 高精度 向 低精度 : 告诉编译器我是故意转换的 , 而且我能确保这样作的后果没有问题 , 你把警告给我关了吧
const_cast<>():
只能改变对象的顶层const属性 , 不能值类型转换:
只有当底层const指向的对象不是 const , 才能进行擦除const :
#include <iostream> using namespace std; int main() { int a = 10; //a 为非const const int & b = a; //底层const , 但是 a 为非const int & c = const_cast<int &>(b); //我们可以擦除掉const 属性 c = 30; cout << a << endl; //30 : 成功修改 const int aa = 10; //aa为const const int & bb = aa; //指出底层const ,底层确实也是const int & cc = const_cast<int &>(bb); //擦除掉底层的const , 但是为cc重新开辟了空间,也就是 cc 和 aa 指向不是同一个内存区域了 cout << aa << endl; //10 cout << cc << endl; return 0; }
dynamic_cast<>()
用于具有继承体系之间的转换 指针和引用 , 类必须具有虚函数 , 父类指针指向的 子类对象向 向子类指针转换 , 或者父类引用指向子类的向子类引用转换使用:
父类指针 指向父类型对象 向 子类型指针转换 | 一个父类的两个子类之间转换 最后为 NULL
父类引用 指向父类型对象 向 子类型引用转换 抛出 std::bad_cast 异常
#include <iostream> class A{ public: virtual ~A(){} } class B :public A{ }; class C :public A{ }; int main() { C * c = new C(); B * b = new B(); A * a = new A(); B *bc = dynamic_cast<B*>(c); //2个子类之间不能 dynamic_cast if(NULL == bc) { cout <<"2 个子类指针之间不能发生转换!" << endl; } B *ba = dynamic_cast<B*>(a); //父类转换成子类 , 不安全 if(NULL == ba) { cout <<"父类指针 指向 父类对象 不能向子类指针转换!"<<endl; } A ra; A & aa = ra; try{ B & b = dynamic_cast<B &>(aa); }catch(std::bad_cast){ cout <<"父类引用 指向 父类对象 不能向子类引用转换!" <<endl; } return 0; } 输出结果 : 2 个子类指针之间不能发生转换! 父类指针 指向 父类对象 不能向子类指针转换! 父类引用 指向 父类对象 不能向子类引用转换!
reinterpret_cast<>():
不建议使用