zoukankan      html  css  js  c++  java
  • c++primer笔记四、表达式

    4.1基础

    基本概念

    一元运算符:作用在一个运算对象(&,)
    二元运算符:作用于两个运算对象(==,
    )
    函数调用也是一种特殊的运算符,对运算对象的数量没有限制。

    重载运算符:用户自定义作用于类类型的作用对象,比如IO库的>><<

    左值与右值

    当一个对象被用作右值, 用的是值(内容);被用作左值时,用的是对象的身份(内存中的位置)。
    需要右值的地方可以用左值替代,但不能把右值当作左值使用。左值当被右值使用时,实际使用的是内容(值)。

    1.复制运算符需要左值,结果也是左值
    2.取地址符作用域左值,返回一个指针是右值
    3.内置的解引用、下标u你算符、迭代器解引用、string和vector的下标运算符结果都是左值
    4.内置类型和得带起的递增递减运算符,作用域左值对象,结果也是左值。
    
    int *p; //p为指针
    decltype(*p);   //*生成左值,结果是int&,
    decltype(&p);   //&生成右值,结果是int**
    
    优先级与结合律

    根据优先级和结合律来决定运算对象组合的方式
    对于没有执行顺序的运算符来说,如果表达式指向并修改了同一个对象,将引发错误并产生未定义的行为。

    int i = 0;
    cout << i << "" << ++i << endl;//未定义的
    

    4种运算符只有当左侧对象值为真才继续求右侧的值。

    &&
    ||
    ?:
    ,
    

    4.2算数运算符

    优先级如下

    +,-    //一元正负号
    *,/,% //乘法除法
    +,-     //加减法
    

    注意:bool值运算是true是1false是0

    bool b = true;
    bool b1 = -b;   //b1也是true,1取负为-1,-1还是true
    

    算数表达式可能会有异常,如除数是0或者溢出。
    取余操作的对象都必须是整数类型。
    在C++11中,整数的商一律向0取整。
    取余操作的时候,正负结果取决于m/n中m的符号

    21 % -5 = 1;
    -21 % -8 = -5;
    

    4.3逻辑关系运算符

    作用于算术类型或指针类型,作用结果能转换成布尔值,值为0的运算对象表示假。
    运算对象和求值结果都是右值

    ! < <= > >= == != && ||
    

    &&和||时,都会短路求值。仅当左侧表达式无法确定结果时,才计算右侧的值。

    expr && expr 左侧为true右侧才执行
    expr || expr 左侧为false右侧才执行
    

    关系运算符不要连用

    if(i < j < k)   //若k大于1则为真
    if(i < j && j < k)  //正确使用
    

    4.4 赋值运算符

    赋值预算符左侧必须是可修改的左值

    int i = 0;            //是初始化不是赋值
    const int ci = i;    //是初始化不是赋值
    
    1025 = i;       //非法
    i = 3.1415;     //类型是int,结果为3
    

    允许用花括号的初始值列表作为赋值的右侧运行对象

    vector<int> vi;
    vi = {0,1,2,3,4,5,6};
    
    赋值满足右结合律

    前提是类型必须相同

    int a,b;
    a = b = 0;  //都是0
    
    int ival,*pval;
    ival = pval = 0;    //错误,指针的值不能赋给int
    

    赋值运算的优先级很低

    复合赋值运算
    += -= *= /= %=   //算术运算发
    <<= >>= &= ^= |=    //位运算符
    
    都等价于
    a = a op b;
    

    唯一的区别是复合运算符只求值1次,普通运算符求值2次。

    4.5递增递减

    两种++i和i++;
    前置版本把对象本身作为左值返回
    后置版本把对象原始值的副本作为右值返回

    除非必须,尽量用前置版本

    auto pbeg = v.begin();
    while (pbeg != v.end && *beg >= 0)
        cout << *pbeg++ << endl;
    

    结果是输出当前的值并将pbeg向前移动一个元素

    后置递增运算符优先级高于解引用,等价于*(pbeg++);
    先把pbeg的值加一,再返回初始值的副本,因此解引用的对象是pbeg未增加之前的值

    c++混用解引用和递增可以更简洁

    cout << *iter++ <<endl;
    等效
    cout << *iter <<endl;
    iter++;
    

    如果一条表达式改变了运算对象的值,另一个表达式又要使用,容易出现错误。

    while(beg != s.end() && !isspace(*beg)))
        *beg = toupper(*beg++); //错误,赋值语句未定义
    

    4.6成员访问运算符

    .(点)和->(箭头)都可用于访问。
    点获取类对象的一个成员
    箭头是点运算的一种简化

    ptr->mem 等价与 (*prt).mem
    

    解引用优先级低于点运算,因此必须要括号。

    4.7条件运算符

    cond ? expr1 :expr2
    

    cond是判断条件的表达式,如果真执行expr1否则执行expr2.

    string finalgrade = (grade < 60) ? "fail" : "pass";
    //低于60是fail,否则就pass
    

    条件运算符优先级很低,如果嵌套再其他语句,需要加括号

    cout << ((grade < 60) ? "fail" : "pass");
    

    4.8位运算

    作用于整数类型,看成二进制位的集合。
    提供检查和设置二进制位的功能。

    `       //位求反
    <<      //左移
    >>      //右移
    &       //位与
    ^       //位异或
    |       //位或
    

    左移右移时,边界职位的都被舍弃

    优先级介于中间,比算数低比关系高,因此要适当加括号

    cout << 42 + 10;     //正确,结果为10
    cout << 10 < 42     //错误,试图比较cout和42
    

    4.9sizeof运算符

    返回表达式或类型名字所占的字节数,返回值时size_t的常量表达式。

    sizeof (type)   //返回类型大小
    sizeof expr     //返回所占空间大小
    

    sizeof结果依赖于其作用的类型

    1.char结果为1
    2.引用类型的结果时被引用对象所占空间的大小
    3.对指针运算得到指针本身所占空间大小
    4.对解引用指针得到指针指向对象所在空间的大小,指针不需要有效
    5.数组返回数组所占空间大小
    6.string或vector结果返回该类型固定部分的大小,不会计算对象中元素占用了多少空间。
    

    可以用数组大小除以单个元素大小得到数组元素个数

    constexpr size_t sz = sizeof(ia)/sizeof(*ia);
    int arr2[sz];
    

    4.10逗号运算符

    含有两个运算对象,从左向右依次求值。
    首先对左侧求值,将结果丢弃。真正结果是右侧表达式的值。
    如果右侧运算对象是左值,最终结果也是左值。
    常常用于for循环

    vector<int> :: size_type cnt = ivec.size();
    for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix, --cnt)
        ivec[ix] = cnt;
    

    每次循环迭代ix和cnt相应改变。

    4.11类型转换

    如果两种类型有关联,可以相互转换。
    隐式转换:根据类型转换规则将运算对象类型同意后进行算术求值。
    隐式转换会尽可能避免损失精度,如果有浮点和整型,整型会变浮点型。

    在以下情况下,编译器会自动转换

    1.大多数表达式中,比int类型小的整型提升为较大的整型
    2.在条件中,非布尔值转成布尔类型
    3.初始化过程中,初始值转成遍历的类型;赋值语句中,右侧转换成左侧的类型
    4.如果运算中有多种类型,都需要转成同一种。
    
    算术转换

    比如一个对象是long double,则另一个都会转成long double。
    整型也会变浮点型

    整数提升

    对于bool、char、signed char、unsighed char、short、unsigned short等类型、只要它们的所有可能指都能存在int中,就会提升会int。否则提升为unsigned int。
    较大的char类型(wchar_t,char16_t,char32_t)提升成int、unsigned int、long、unsigned long、long long 和unsigned longlong中最小的一种。

    无符号类型的运算对象

    如果一个运算对象无符号,一个有符号,而无符号的类型不小于带符号的类型,则带符号的会转换成无符号来的。
    带符号的大于无符号的,则转换结果依赖于机器。如果无符号的所有制都能存放在带符号类型中,则无符号转换成带符号。否则就带符号类型的运算对象转换成无符号的。

    例如:

    long和unsigned int, int和long大小相同,则long转成unsigned int;如果long类型占用的空间比int多,则unsigned int转成long。
    
    其他隐式类型的转换
    数组转换指针

    数值会自动转换成首元素的指针

    int *ip = ia;
    
    指针的转换

    0或者nullptr可以转换成任意指针类型;
    指向任意非常累的指针能转换成void
    指向任意对象的指针能转换成const void

    转成布尔类型

    如果指针或算数类型为0,转成false。

    转成常量

    允许将指向非常量类型的指针转换成指向常量类型的指针,引用同理。

    类类型定义的转换

    有编译器自动执行,但是每次只执行一种

    显式转换

    也称强制类型转换(cast)

    int j,j;
    double slope = i / j;
    

    命名的强制类型转换如下形式

    cast-name<type>(exoression);
    
    tpye是转换的目标类型
    expression是要转换的值。
    如果type是引用,则结果是左值。
    cast-name是 static_cast,dynamic_cast,const_cast和reinterpret_cast
    
    static_cast

    任何明确定义的类型转换,只要不包含底层const,都可以使用static_cast。

    double slope = static_cast<double>(j) / i;
    

    当需要把较大的算术类型赋值给小类型,很有用。
    强制类型转换告诉读者和编译器,不在乎精度损失,因此警告会被关闭。

    对于编译器无法自动执行的,static_cast也能做到。例:

    //找回存在void*指针的值
    void *p = &d;
    double *dp = static_cast<double*>(p);
    //把指针存放在void*中,并用static_cast强转回来是,应该确保指针值不变。
    //即转换的结果和原始地址相等,因此要确保转换后所得类型就是指针所指类型。
    
    const_cast

    只能改变运算对象的底层const
    用于去掉const性质。

    const char *cp;
    const_cast<char*>(cp);      
    //但是不用用来挟制,是未定义的行为
    char *p = const_cast<char*>(pc);
    static_cast<string>(cp);    //正确,字符串字面值转换成string
    const_cast<string>(cp);     //错误,const_cast只能改变常量属性
    
    reinterpret_cast

    为运算对象的位模式提供较低层次上的重新解释

    int num = 0x00636261;//用16进制表示32位int,0x61是字符'a'的ASCII码
    int * pnum = &num;
    char * pstr = reinterpret_cast<char *>(pnum);
    cout<<"pnum指针的值: "<<pnum<<endl;
    cout<<"pstr指针的值: "<<static_cast<void *>(pstr)<<endl;//直接输出pstr会输出其指向的字符串,这里的类型转换是为了保证输出pstr的值
    cout<<"pnum指向的内容: "<<hex<<*pnum<<endl;
    cout<<"pstr指向的内容: "<<pstr<<endl;
    
    //结果为
    0x7ffcfa7e861c
    0x7ffcfa7e861c
    636261
    abc
    

    两个指针的值是完全相同的,只是内容不一样。

    字符串指针要遇到""才表示结束。

    如果num值为0x63006261,输出为ab
    如果num值为0x64636261,输出就不确定,可能出错。
    

    尽量少用。

    dynamic_cast

    支持运行时的类型识别

  • 相关阅读:
    四则运算程序总结
    软件体系结构第三章-解释器风格
    软件工程概论随笔2
    软件体系结构第二章随笔
    软件体系结构第一章随笔
    关于如何衡量个人在各自团队的效率和绩效
    查询sql数据库中表占用的空间大小
    如何调试触发器
    sql server查看某个表上的触发器
    触发器deleted 表和 inserted 表详解
  • 原文地址:https://www.cnblogs.com/aqq2828/p/13964239.html
Copyright © 2011-2022 走看看