保持快乐,善于表达,敢于创新

chapter4 expression

上一篇 / 下一篇  2011-09-09 09:58:20 / 个人分类:c++

1. %操作符
%操作符计算两个数相除的余数第一个数被第二个数除.该操作符只能被应用在整值类型(char short int 和long)的操作数上(float, double 不可). 当两个操作数都是正数时结果为正,但是如果有一个或两个操作数为负余数的符号则取决于机器.
3.14 % 3; // 编译时刻错误: 浮点操作数
21 % 6; // ok: 结果是 3
21 % 7; // ok: 结果是 0
21 % -5; // 机器相关: 结果为 -1 或 1

2. 显式类型转换(explicit type conversion)或强制类型转换(cast), 强制转换使编译器把一个对象或表达式从它当前的类型转换成程序员指定的类型. 在这种情况下我们把byte_value 转换成一个int 型的对象:
char byte_value = 0;//ascii 0表示null
static_cast<int> ( byte_value ) //这样就转化为int 0, 其实相当于ascii表的0的二进制数.
byte_value: 0

3. 除法
double dval1 = 10.0, dval2 = 3.0;
int ival1 = 10, ival2 = 3;
dval1 / dval2;   //double运算,结果仍为double,结果为3.33333
ival1 / ival2; //int型运算,结果仍为整形,结果为3
dval1 / ival1 //将ival1隐式装换为double,然后再double运算,结果为3.3333

4. bool运算:
a. C++语言本身并没有定义计算顺序,所以下面表达式可以从左向右,也可以从右向左.
if ( ia[ index++ ] < ia[ index ] )
程序员假设左边的操作数先计算因此比较ia[0]是否小于ia[1],但, C 或C++语言
并不保证从左到右的计算顺序. 实际上的实现可能是先计算右边的操作数在这种情况下ia[0]与它自已作比较, 而ia[1]从来没有被计算. 应更改为下面表达式:
if ( ia[ index ] < ia[ index+1 ] )
// 交换元素
++index;
b. if ( ival != jval != kval )//错误,原因是第一个不等于表达式的左操作数为true 或false,它是第一个表达式的结果——即kval被测试是否与转换来的0 或1 不相等.
应该改为:
if ( ival != jval && ival != kval && jval != kval )
// 省略其他代码

5. 初始化和付值
1. 一个对象初始化只能有一次,在声明过程中; 而付值可以有很多次.
int ival = 1024; //分配int空间,命名为ival,把其值存为1024
int *pi = 0;//分配int指针空间,命名为pi,其指针值不存任何值.
ival = 2048; //将2048整型值付给ival的值.
pi = &ival; //将ival的地址付给pi的值.
2. 付值规则:
右边表达式的类型必须与左边被赋值对象的类型完全匹配.不一致需要转换,以转换为左表达式类型一致为止.
int i = 3.241 //将值3.241 double类值隐式转换为int,然后付给i的值.
3. 连着付值,必须是同种类型.
float fval;
int ival;
int *pi;
fval = ival = pi = 0; //错误,因为fval和ival不是同类型,但编译器可以隐式把int转换为float,但pi是指针类型,故不可以.
fval=ival=0; //可以.
ival=pi=0; //不可以.

6. ++ 和--:
后置表示先使用,用完再加1;
a++, //先用a值,用完后再将a+1
前值表示将值加1后,再使用它
++a, //先把a+1, 然后使用它.

7. sizeof:
1. 三个表达式:
sizeof (type name );
sizeof ( object );
sizeof object; //数组首地址
返回一个对象或类型名的字节长度,注意是字节长度,而不是对象或类型名的元素个数, 如下:
#include <cstddef>
int ia[] = { 0, 1, 2 };
size_t array_size = sizeof ia; // sizeof 返回整个数组的所占字节大小
size_t element_size = array_size / sizeof( int ); // sizeof 返回一个int 类型所占字节的大小, 此表达式结果为数组中的元素个数.
2.
int *pi = new int[ 3 ];
size_t pointer_size = sizeof ( pi ); //返回pi指针所占的字节,而非数组所占字节.
cout << "short :\t" << sizeof(short) << endl; //short占2字节
cout << "short* :\t" << sizeof(short*) << endl;//指向short的指针占4个字节
cout << "short& :\t" << sizeof(short&) << endl;//引用short,其实就是short类型,占2个字节
cout << "short[3] :\t" << sizeof(short[3]) << endl;//引用short的数组,共有三个元素,每个元素2个字节,故共6个字节.
应用在指针类型上的sizeof操作符,返回的是包含该类型地址所需的内存长度. 但是应用在引用类型上的sizeof 操作符, 返回的是包含被引用对象所需的内存长度.
3. sizeof 操作符在编译时刻计算,因此被看作是常量表达式,故可以用在数组声明中:
int array[sizeof( some_type_T )];// ok: 编译时刻常量表达式, 红色部分为常量,在编译时刻就可以计算出.

8. new and delete:
在heap上动态分配对象内存,然后将其地址付给stack上的指针对象.
int *pi = new int; //从空闲存储区中分配了一个int 型的对象, 并用它的地址初始化pi. 并没有给此对象初始化
int *pi = new int( 1024 ); //不但分配了这个对象而且用1024 将其初始化.
string *psa = new string[ 10 ];//空闲存储区分配了一个含有10 个string 类对象的数组, 用它的地址初始化psa 然后依次在每个元素上调用string 类的缺省构造函数.
delete pi //归还heap内存.
delete[] psa //释放了pia 指向的10 个int 对象的数组, 并把相关的内存区返还给空闲存储区.

9. 转换
1. 隐式转换:隐式类型转换发生在下列这些典型的情况下:
1)在混合类型的算术表达式中,在这种情况下最宽的数据类型成为目标转换类型.
int ival = 3;
double dval = 3.14159;
ival + dval;// ival 被提升为double 类型: 3.0
2). 用一种类型的表达式赋值给另一种类型的对象,在这种情况下目标转换类型是被
赋值对象的类型.
int *pi = 0;// int 0 被转换成int*类型的空指针值 ascii的null;
3). 把一个表达式传递给一个函数调用,表达式的类型与形式参数的类型不相同.在这
种情况下目标转换类型是形式参数的类型.
extern double sqrt( double );
cout << "The square root of 2 is "
<< sqrt( 2 ) << endl;    // 2 被提升为double 类型: 2.0
4). 从一个函数返回一个表达式表达式的类型与返回类型不相同. 在这种情况下目
标转换类型是函数的返回类型.
double difference( int ival1, int ival2 )
{
  return ival1 - ival2; // 返回值被提升为double 类型
}
2. 算术运算规则:
1).为防止精度损失, 如果必要的话类型总是被提升为较宽的类型.
2).所有含有小于整型的有序类型的算术表达式,在计算之前其类型都会被转换成整
型(如: char signed char unsigned char 和short int 都被提升为类型 int).
3.显式转换
  显式转换也被称为强制类型转换cast, 包括下列命名的强制类型转换操作符
static_cast dynamic_cast const_cast 和reinterpret_cast.
1). void*: 任何非const 数据类型的指针都可以被赋值给void*型的指针. void*型的指针被称为泛型generic 指针,因为它可以指向任意数据类型的指针.
int ival;
int *pi = 0;
char *pc = 0;
void *pv;
pv = pi; // ok: 隐式转换
pv = pc; // ok: 隐式转换
const int *pci = &ival;
pv = pci; // 错误: pv 不是一个const void*.
const void *pcv = pci; // ok
void*的指针必须先被转换成某种特定类型的指针, 在C++中, 不存在从void*型指针到特殊类型的指针之间的自动转换.
#include <cstring>
int ival = 1024;
void *pv;
int *pi = &ival;
const char *pc = "a casting call";
void mumble()
{
pv = pi; // ok: pv 指向 ival void*类型可以指向任何对象.
pc = pv; // 错误: 没有标准的转换, pv 指向一个整数而不是一个字符数组.
pc = static_cast< char* >( pv );//仍然错误,但编译器不用报错了,可以通过编译.
char *pstr = new char[ strlen( pc )+1 ];
strcpy( pstr, pc );
}

2). const_cast: 将转换掉表达式的常量性(以及volatile 对象的volatile性).
extern char *string_copy( char* );
const char *pc_str;
char *pc = string_copy( const_cast< char* >( pc_str ));

3). 编译器隐式执行的任何类型转换都可以由static_cast 显式完成
double d = 97.0;
char ch = static_cast< char >( d );
因为从一个较大类型到一个较小类型的赋值会导致编译器产生一个警告以提醒我们潜在的精度损失, 当我们提供显式强制转换时警告消息被关闭.

4). reinterpre_cast 通常对于操作数的位模式执行一个比较低层次的重新解释,它的正确性很大程度上依赖于程序员的主动管理.
complex<double> *pcom;
char *pc = reinterpret_cast< char* >( pcom ); //将一个枚举类型转换为字符类型。
相当于:char *pc = (char*) pcom;

10. :冒号构造函数付值
class iStack {
public:
  iStack( int capacity ): _stack( capacity ), _top( 0 ) {}
  bool pop( int &value );
  bool push( int value );
  bool full();
  bool empty();
  void display();
  int size();
private:
  int _top;
  vector< int > _stack;
};
其中构造函数iStack,相当于:
  iStack(int capacity){
  this->_stack(capacity);
  this->_top=0;
}
:冒号除了继承作用,三目运算,还有为构造函数付值,如果对父类的变量付值,必须用冒号付值方式。



TAG:

 

评分:0

我来说两句

Open Toolbar