C++ Primer 学习(第四章)

1.左值和右值

C++中可以这样简单的理解左值和右值:能够取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。一个重要的原则是在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。当一个左值被当成右值使用时,实际使用的是它的内容(值)。

2.C++中整除:(-m)/n和m/(-n)都等于-(m/n)。

取余运算:如果%左边的操作数为负数时,结果为负数或0;如果%左边的操作数为正数时,结果为正数或0

例如:

21/-5;//结果为-4,即-(21/5)=-4
-21/5;//结果为-4,即-(21/5)=-4
-21%-8;//结果为-5,而不是3
21%-8;//结果为5,而不是-3

3.因为后置递增运算符的优先级高于解引用运算符,所以*p++等价于*(p++)

因为解引用运算符的优先级低于点运算符,所以执行解引用运算的子表达式两端必须加上括号。例如:

*p.size();//错误,p是一个指针,它没有名为size的成员

4.条件运算符的优先级非常低,因此当一条长表达式中嵌套了条件运算子表达式时,通常需要在它两端加上括号。

注意观察下述代码:

cout<<((grade<60) ? "fail":"pass";//输出pass或者fail
cout<<(grade<60) ? "fail":"pass";//输出1或者0
cout<<grade<60 ? "fail":"pass";//错误:试图比较cout和60

上述代码第2行,grade和60的比较结果是<<运算符的运算对象,因此如果grade<60为真输出1,否则输出0。<<运算符的返回值是cout,接下来cout作为条件运算符的条件。所以第2行代码等价于下述代码:

cout<<(grade<60);//输出1和0
cout ? "fail":"pass";//根据cout的值是true还是false产生对应的字面值

 上述代码第3行等价于下述代码:

cout<<grade;//小于运算符的优先级低于移位运算符,所以先输出grade
cout < 60 ? "fail":"pass";//然后比较cout和60,错误

5.sizeof运算符:

sizeof运算符返回一条表达式或一个类型名字所占的字节数,运算符的运算对象有两种形式:

sizeof (type)
sizeof expr

其中,第2种形式中,sizeof返回的是表达式结果类型的大小。与众不同的一点是,sizeof并不实际计算其运算对象的值

sizeof运算符的结果部分依赖于其作用的类型:

  • 对char或者类型为char的表达式执行sizeof运算,结果为1
  • 对引用类型执行sizeof运算得到被引用对象所占空间的大小
  • 对指针执行sizeof运算得到指针本身所占空间的大小
  • 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需要有效。sizeof解引用一个无效指针是一种安全行为,因为指针实际上并没有真正使用,sizeof不需要真的解引用指针也能知道它所指对象的类型。
  • 数组执行sizeof运算得到整个数组所占空间的大小,等价于对所有的元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转化为指针来处理
  • 对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。

知道上面的一些特性,可以利用sizeof做一些事情,例如求数组中的元素的个数。

例如:

int x[10];
cout<<sizeof(x)/sizeof(*x)<<endl;

上述代码输出结果为10,其中,sizeof(x)返回数组中所有元素所占空间的大小,sizeof(*x)返回数组中单个元素所占空间的大小,故相除可以得到数组中元素的个数。

6.命名的强制类型转换

格式:

cast-name<type>(expression)

其中,type是转换的目标类型,expression是要转换的值。cast-name是static_castdynamic_castconst_castreinterpret_cast中的一种。

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

例如:

int i=2,j=5;
double slope=ststic_cast<double>(j) / i;

上述代码中,把整型的j强制转化为double类型的j。

又如:

void* p=&d;
double *dp=static_cast<double*>(p);

上述代码中,由于p是一个void指针,其可以存放任意类型对象的地址,但是无法操作其所指的对象。通过上述代码,就可以把指针强制转化为double类型的指针。需要注意的是,转换后得到的类型必须是指针所指的类型。(我的理解是转换后的double类型应该和指针所指的d的类型一致,即d是double类型,我们才能把void指针转换为double类型)。

const_cast:const_cast只能改变运算对象的底层const,不能改变对象的类型,只有const_cast能改变对象的常量属性。

注意下述代码:

const char *cp;
char *q=static_cast<char*>(cp);//错误,static_cast不能改变常量属性
static_cast<string>(cp);//正确,字符串字面值转换成了string类型,只是改变了类型,没有改变const性质
const_cast<string>(cp);//错误:const_cast不能改变类型,只能改变常量属性

需要注意的是,如果对象本身不是一个常量,使用强制类型转换获得写权限是合法的行为。然而如果对象是一个常量,再使用const_cast执行写操作就会产生未定义的结果。

例如:

const int j=3;
int *pj=const_cast<int*>(&j);
*pj=4;//错误,因为其用去掉const性质的指针去修改一个原本就是常量的值

猜你喜欢

转载自blog.csdn.net/lovebasamessi/article/details/82792962