C++ primer 5th : 第四章笔记

mac2022-06-30  23

第四章: 表达式

  基本概念:

    运算符:  一元 , 二元 , 三元

    组合运算符 和 运算对象 : 

        优先级:  使用  () 避免优先级的混淆的问题

        结合律:  

        求值顺序: 二元运算符两边的表达式计算顺序可能存在差异 , 应该避免

    对优先级 , 结合率 , 求值顺序的解释:

        

//如下表达式: //我们假设 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; } ~ decltype 的是的左值类型

 


 

算数运算符:

  略


 

逻辑运算符:

  注意 : &&  || 有运算符求值顺序的规定 , 先算左边在算右边 ,  左边满足的顺序右边不再计算 , 这就是短路

 


 

赋值运算符:

 = 左边必须是一个可以修改的左值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<>():

      不建议使用 

 

转载于:https://www.cnblogs.com/Geek-Z/p/9935424.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)