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

第四章: 表达式

  基本概念:

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

    组合运算符 和 运算对象 : 

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

        结合律:  

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

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

        

 //如下表达式:
 //我们假设 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