【C++Primer学习笔记】第4章表达式_中年程序员的空间_百度空间

=======================================================
4.1 什么是表达式
=======================================================
表达式由一个或多个 操作数(aperand) 、以及应用在这些操作数上的操作构成。
应用在操作数上的操作由 操作符(operator) 表示。分:一元操作符和二元操作符。


=======================================================
4.2 算术操作符
=======================================================
操作符   功能   用法   说明
*   乘   expr * expr
/   除   expr / expr
%   求余(取模) expr % expr 只能用于整数,若有一个或两个数为负,余数的是否为负则取决于机器
+   加   expr + expr
-   减   expr - expr

标准C++头文件limits提供了与内置类型表示有关的信息,如一个类型能表示的{zd0}值和最小值。
标准C头文件climits和cfloat定义了提供类似信息的预处理器宏。


=======================================================
4.3 等于、关系和逻辑操作符
=======================================================
操作符   功能   用法
!   逻辑非   !expr
<   小于   expr < expr
<=   小于等于 expr <= expr
>   大于   expr > expr
>=   大于等于 expr >= expr
==   等于   expr == expr
!=   不等于   expr != expr
&&   逻辑与   expr && expr
||   逻辑或   expr || expr
注:这些操作符的结果是bool类型(true或false)。

int elem_cnt = 0;
vector<int>::iterator iter = ivec.begin();
while( iter != ivec.end() )
{
    elem_cnt += *iter < some_value;
    ++iter;
}

二元操作符存在潜在的缺点:左右操作数的计算顺序是未定义的,因此计算过程必须是与顺序无关的:
if( ia[index++] < ia[index] ) ... //目的是ia[0]与ia[1],但实际上很可能是ia[0]与它自己
所以应该写成:
if( ia[index] < ia[index+1] ) ...


=======================================================
4.4 赋值操作符
=======================================================
int ival = 1024;//初始化操作,只可以被初始化一次
ival = 2048; //赋值操作,可以被重复多次

当我们把不同类型的表达式赋值给一个对象时,编译器会试着隐式将右操作数转换成被赋值对象的类型,如果可能的话。若不能则编译出错。
ival= 3.1415926; //3
int *pi;
pi = ival; //错误

赋值表达式可以被当作一个子表达式:
while( (ch=next_char()) != '\n' ) ... //外加的小括号是必需的

赋值操作符也可以被连接在一起,只要操作数都是相同的类型:
int ival, jval, *pval;
ival = jval = 0; //ok
ival = pval = 0; //错误,即使单独赋值都是正确的

int ival = jval = 0; // 可能合法,也可能不合法,因为jval必须实现定义成适当的类型
应该写成:
int ival=0, jval=0;

复合赋值操作符:
+=    -= *=   /= %=
<<=   >>= &=   ^= |=


=======================================================
4.5 递增和递减操作符
=======================================================
递增(++)、递减(--)

为什么C++不叫++C?
转自:
C++的C说明了它本质上是从C语言演化过来的,C++语言是C语言的超集,是在C语言基础上进行的扩展(引入了new、delete等C语言中没有的操作符,增加了面向对象程序设计的支持,等等),是先有C语言,在进行C++。根据自增操作符前、后置形式的差别,C++表示对C语言进行扩展之后,还可以使用C语言的内容,而写成++C则表示无法再使用C的原始值了,也就是说C++不能向下兼容C了,这与实际情况不符。


=======================================================
4.6 复数操作
=======================================================
C++不但支持一般的算术操作符,如加、减、乘、除,而且还支持复数类型与内置类型的混合运算。如:
#include <complex>
complex<double> a;
complex<double> b;
//...
complex<double> c = a*b + a/b;
complex<double> complex_obj = a + 3.14159;
或:
double dval = 3.14159;
complex_obj = dval;
但是反过来则不行:
dval = complex_obj; //错误

可以显示的知名我们要用复数对象的哪部分来赋值:
double re = complex_obj.real();
double im = complex_obj.imag();

double re = real(complex_obj);
double im = imag(complex_obj);

支持四种复合赋值:+=、-=、*=、/=

复数的输出:
complex<double> complex0(3.14159, -2.171);
cout << complex0 << endl;
输出:
(3.14159, -2.171)

复数的输入,下面任意格式都可以:
cin >> a >> b >> c;
3.14159 (3.14159) (3.14, -1.0)

复数类支持的其他操作包括:sqrt()、abs()、polar()、sin()、cos()、tan()、exp()、log()、log10(),以及pow()。


=======================================================
4.7 条件操作符
=======================================================
expr1 ? expr2 : expr3;

使用:
int i=10, j=20, k=30;
cout << "the larger value of " << i << " and " << j << " is " << (i>j ? i:j) << endl;

条件操作符可以被嵌套,但是深度的嵌套比较难读:
//max被设置为3个变量中的{zd0}值
int max = ( (i>j) ? ((i>k) ? i : k) : (j<k) ? j : k );


=======================================================
4.8 sizeof操作符
=======================================================
sizeof操作符的作用是返回一个对象或类型名的 字节长度 。它有三种形式:
sizeof(type name);
sizeof( object );
sizeof object;
返回值的类型是size_t,这时一种与机器相关的typedef定义,我们可以在cstddef头文件中找到它的定义。

使用:
#include <cstddef>
int ia[] = {1, 2, 3};
size_t array_size = sizeof ia; //整个数组占的字节长度:12
size_t element_size = array_size / sizeof(int); //int类型的大小

int *pi = new int[3];
size_t pointer_size = sizeof(pi); //返回指向int型的指针的字节长度,而不是pi指向的数组的长度

sizeof操作符在编译时刻计算,因此被看作是常量表达式。它可以用在任何需要常量表达式的地方:
int array[sizeof(some_type_T)]; //数组的维数

例子:
#include <string>
#include <iostream>
#include <cstddef>
int main()
{
    size_t ia;
    ia = sizeof( ia ); // ok
    ia = sizeof ia; // ok

    // ia = sizeof int; // 错误
    ia = sizeof( int ); // ok
    int *pi = new int[ 12 ];
    cout << "pi: " << sizeof( pi )
         << " *pi: " << sizeof( *pi )
         << endl;

    // 一个 string的大小与它所指的字符串的长度无关
    string st1( "foobar" );
    string st2( "a mighty oak" );
    string *ps = &st1;
    cout << "st1: "     << sizeof( st1 )
         << " st2: "     << sizeof( st2 )
         << " ps: "      << sizeof( ps )
         << " *ps: "     << sizeof( *ps )
         << endl;
    cout << "short :\t" << sizeof(short) << endl;
    cout << "short* :\t"<< sizeof(short*)       << endl;
    cout << "short& :\t"<< sizeof(short&)       << endl;
    cout << "short[3] :\t" << sizeof(short[3]) << endl;
    cout << "char :\t" << sizeof(char) << endl;
}

编译并运行它 产生如下结果:
    pi: 4 *pi: 4
    st1: 12 st2: 12 ps: 4 *ps: 12 //试验得出的结果是:st1: 4 st2: 4 ps: 4 *ps: 4
    short : 2
    short* : 4
    short& : 2
    short[3] : 6
    char : 1


=======================================================
4.9 new和delete表达式
=======================================================
系统为每个程序都提供了一个在程序执行时可用的内存池,成为 程序的空闲存储区 或 堆。
运行时刻的内存分配被称为 动态内存分配。在空闲存储区里分配。
动态分配有new完成。

int *pi = new int; //没有被初始化
int *pi = new int(1024);//初始化为1024
int *pia = new int[10]; //定义数组,同样没有初始化,若是类对象数组且有缺省构造函数,则依次应用在每个元素上

当对象完成了使命时,我们必须显示地把对象的内存返还给空闲存储区。用delete。但是它应该用在new分配的指针上(成对出现)。
在类对象上使用delete时,会应用其析构函数释放存储区,并还给空闲存储区。
delete pi;
delete [] pia; //释放数组


=======================================================
4.10 逗号操作符
=======================================================
逗号表达式是一系列由逗号分开的表达式。这些表达式从左向右计算。逗号表达式的结束是最右边表达式的值。

例子,{dy}个逗号表达式的值是ix,第二个是0:
int ival = (ia!=0) ? ix=get_value(), ia[index]=ix : ia=new int[sz], ia[index]=0;


=======================================================
4.11 位操作符
=======================================================
操作符   功能   用法
~   按位非   ~expr
<<   左移   expr1 << expr2
>>   右移   expr1 >> expr2
&   按位与   expr1 & expr2
^   按位异或 expr1 ^ expr2
|   按位或   expr1 | expr2
&=   按位与赋值 expr1 &= expr2
^=   按位异或赋值 expr1 ^= expr2
|=   按位或赋值 expr1 |= expr2

位操作符允许程序员设置或测试独立的位或位域。
如果一个对象被用作一组位或位域的离散集合,那么这样的对象成为 位向量。

C++有两种方式支持位向量:
1. 用内置整值类型来表示,如:unsigned int;
2. 标准库提供了一个bitset类,它支持位向量的类抽象。

用内置整值类型来表示:
类型可以是有符号的,也可以是无符号的,建议使用无符号类型。
操作符   功能
~   翻转操作数的每一位
<<, >>   将其左边操作数的位想左或右移动某些位。左移符从右边开始补0;右移,无符号数则补0,有符号数补0或符号位的拷贝视具体实现定义
&   两个整值操作数。在每个位所在处与
|   两个整值操作数。在每个位所在处或
^   两个整值操作数。在每个位所在处非,两个数只有一个含有1,则结果该位为1,否则为0

例子:
unsigned int quiz1 = 0;
quiz1 |= 1 << 27; //将第28位置成1
quiz1 &= ~(1 << 27); //将第28位置成0
bool hasPassed = quiz1 & (1<<27); //判断第28位是1还是0


=======================================================
4.12 bitset操作
=======================================================
继续上节,用bitset类进行位操作。

操作   功能    用法   说明
test( pos ) pos 位是否为1   a.test( 4 )
any()   任意位是否为1   a.any()   有一位或多位为1时,返回true,否则false
none()   是否没有位为1   a.none() 与any()相反,都为0才返回true
count()   值是1 的位的个数 a.count()
size()   位元素的个数   a.size()
[pos]   访问pos 位   a[ 4 ]
flip()   翻转所有的位   a.flip()
flip( pos ) 翻转pos 位   a.flip( 4 )
set()   将所有位置1   a.set()
set( pos ) 将pos 位置1   a.set( 4 )
reset()   将所有位置0   a.reset()
reset(pos) 将pos 位置0   a.reset( 4 )

头文件:<bitset>

定义和初始化:
bitset<32> bitvec; //缺省定义,指名位向量的长度是32,所有为被初始化为0
bitset< 32 > bitvec2( 0xffff ); //前N位被初始化为参数的相应位值
bitset< 32 > bitvec3( 012 ); //012为8进制数,将第1 和3 位的值设置为1,其它为0,同样可以用10进制数
string bitval( "1010" );
bitset< 32 > bitvec4( bitval ); //同上
我们还可以标记用来初始化bitset 的字符串的范围:
string bitval( "1111110101100011010101" );
bitset< 32 > bitvec5( bitval, 6, 4 ); //从低位开始数,从位置6开始, 长度为4: 1010
bitset< 32 > bitvec6( bitval, 6 ); //从低位开始数,从位置6开始直到{zh1}: 1010101
bitset类还支持位操作符:
bitset<32> bitvec7 = bitvec2 | bitvec3;

针对上节的例子,现在可以写成:
bitset<30> quiz1;
quiz1.set(27); //quiz1 |= 1 << 27;将第28位置成1
或:quiz1[27] = 1;
quiz1.reset(27); //quiz1 &= ~(1 << 27);将第28位置成0
或:quiz1[27] = 0;
if(quiz1[27]); //判断第28位是1还是0
或:if(quiz1.test(27))

还包括两个类型转换函数:
to_string(); //转换成string
to_ulong(); //转换成 unsigned long


=======================================================
4.13 优先级
=======================================================
操作符优先级是指复合表达式中操作符计算的顺序。
用括号把一些子表达式括起来,可以改变优先级。

结合性:
赋值操作是右结合的:
ival = jval = kval = lval
算术操作符是左结合的:
ival + jval + kval + lval

C++操作符的全集,每段中优先级相同,上段优先级高于下段:
操作符    功能    用法
::    全局域    ::name
::    类域    ::name
::    名字空间域   namespace::name
.    成员选择   object.member
->    成员选择   pointer->member
[]    下标    variable[ expr ]
()    函数调用   name(expr_list)
()    类型构造   type(expr_list)
++    后置递增   lvalue++
--    后置递减   lvalue--
typeid    类型ID    typeid(type)
typeid    运行时刻类型 ID   typeid(expr)
const_cast   类型转换   const_cast<type>(expr)
dynamic_cast   类型转换   dynamic_cast<type>(expr)
reinterpret_cast 类型转换   reinterpret_cast<type>(expr)
static_cast   类型转换   static_cast<type>(expr)
sizeof    对象的大小   sizeof object
sizeof    类型的大小   sizeof( type )
++    前置递增   ++lvalue
--    前置递减   --lvalue
~    按位非    ~expr
!    逻辑非    !expr
-    一元减    -expr
+    一元加    +expr
*    解引用    &expr
&    取地址    &expr
()    类型转换   (type)expr
new    分配对象   new type
new    分配/初始化对象   new type(expr_list)
new    分配/替换对象   new(expr_list)type(expr_list)
new    分配数组   所有的形式
delete    释放对象   所有的形式
delete    释放数组   所有的形式
->*    指向成员选择   pointer->*pointer_to_member
.*    指向成员选择   object.*pointer_to_member
*    乘    expr * expr
/    除    expr / expr
%    取模 求余   expr % expr
+    加    expr + expr
-    减    expr - expr
<<    按位左移   expr << expr
>>    按位右移   expr >> expr
<    小于    expr < expr
<=    小于等于   expr <= expr
>    大于    expr > expr
>=    大于等于   expr >= expr
==    等于    expr == expr
!=    不等于    expr != expr
&    按位与    expr & expr
^    按位异或   expr ^ expr
|    按位或    expr | expr
&&    逻辑与    expr && expr
||    逻辑或    expr || expr
?:    条件表达式   expr ? expr : expr
=    赋值    lvalue = expr
=,*=,/=,%=,+=,-=,<<=,>>=,&=,|=,^= 复合赋值 lvalue += expr 等
throw    抛出异常   throw expr
,    逗号    expr, expr


=======================================================
4.14 类型转换
=======================================================
隐式转换:
int ival = 0;
ival = 3.541 + 3;
这里3.541是double型,3是int型。C++在计算前运用“算术转换”将两个操作数转成同类型。原则是,小类型总是被提升成大类型。
本例3被提升为double型。
所以加法的结果是6.351。
赋值给ival是再转型,右边转到左边,自动按截取而不是舍入进行,小数被抛弃。
所以结果是6。

若想保住小数,使其舍入,则需要自己程序实现:
ival = 3.541 + 3 + 0.5; //保证舍入
也可以显示的先类型转换再计算:
ival = static_cast<int>( 3.541 ) + 3; //结果仍然是6,显示转换也是舍弃小数

1. 隐式类型转换:
在下列情况下会发生隐式转换:
(1)在混合类型的算术表达式中:最宽的数据类型成为目标转换类型,这也被称为 算术转换。
(2)用一种类型的表达式赋值给另一种类型的对象:目标转换类型是被赋值类型。
(3)把一个表达式传递给一个函数调用:转成形式参数的类型。
(4)从一个函数返回一个表达式,表达式的类型与返回类型不同:转成返回类型。

2.算术转换:
算术转换保证了二元操作符的两个操作数被提升为共同的类型,然后再用它表示结果的类型。原则:
(1)为防止精度损失,如果必要的话,类型总是被提升为较宽的类型。
(2)所有含有小于整型的有序类型的算术表达式,在计算之前,其类型都会被转换成整型。包括:char、singed char、unsigned char、short int,及枚举型enum。
如:
3.14159L + 'a'; //3.14159L是long double型,‘a’先被转成long bouble(ASC码为97)再计算

3. 显示转换
也被称为 强制类型转换(cast),包括下列操作符:
static_cast、dynami_cast、const_cast、reinterpret_cast。
显示转换之后,编译器则不会发出提示信息了(否则有警告)。

一般形式:
cast-name<type>(expression);

显示转换存在的原因:
(1)将void*强制转成显示类型的指针
(2)希望改变通常的标准转换
(3)要避免出现多种转换可能的歧义情况
如:
const char *pc_str;
char *pc = pc_str; //编译出错
char *pc = const_cast<char*>(pc_str); //ok

行为不佳的静态转换(即那些有潜在危险的类型转换):
将void*型的指针强制转换成某种显式指针类型
把一个算术值强制转换成枚举型
把一个基类强制转换成其派生类或者这种类的指针或引用

在引入这些强制转换操作符之前,显式强制转换由一对括号来完成:
char *pc = (char*) pcom; //与reinterpret_cast效果一样

4. 旧式强制类型转换
有两种形式:
type (expr); //C++语言强制转换符号
(type) expr; //C语言强制转换符号

标准C++对旧式强制转换符号的支持,是为了保持 向后兼容性。
如果我们希望自己的代码在C++和C语言中都能编译的话,那么只能使用C语言的强制转换符号。
但是建议使用新式强制转换符号。


=======================================================
4.15 栈类实例
=======================================================
栈是计算机科学的一个基本数据抽象,它允许以 后进先出 的顺序嵌入和获取其中的值。

代码略。



郑重声明:资讯 【【C++Primer学习笔记】第4章表达式_中年程序员的空间_百度空间】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——