《C++ Primer中文版(第五版)》 第四章 表达式

《C++ Primer中文版(第五版)》 第四章 表达式

基础

基本概念

什么是一元,二元和三元运算符?

一元运算符:作用于一个运算对象的运算符
二元运算符:作用于两个运算对象的运算符
三元运算符:作用于三个运算对象的运算符
补充说明:一个符号到底是一元还是二元运算符 由它的上下文决定。

优先级和结合律

什么是复合表达式?

复合表达式是指含有两个或者多个运算符的表达式。

求值顺序

处理复合表达式的建议:
1.拿不准的时候最好用括号来强制让表达式的组合关系复合程序逻辑要求。
2.如果改变了运算对象的值,在表达式的其他地方不要再使用这个运算对象。
(例外:当改变运算对象的子表达式本身就是另外一个子表达式的运算对象式该规则无效)

算术运算符《C++ Primer中文版(第五版)》 第四章 表达式_第1张图片

补充说明:
若无特殊说明,算术运算符能够作用于任意算术类型以及任意能够转换为算术类型的类型。
在表达式求值之前,小整数类型的运算对象被提升成较大整型数类型,所有运算对象最终会转换成同一类型。

逻辑和关系运算符《C++ Primer中文版(第五版)》 第四章 表达式_第2张图片

补充说明:
&& 和||都是先求左侧运算对象的值再求右侧运算对象的值,当且仅当左侧运算对象无法确定表达式的结果时,才会计算右侧运算对象的值。这种策略称为短路求值

赋值运算符

注意事项:
赋值运算符的左侧运算对象必须是一个可修改的左值。
如果赋值运算符的左右两个运算对象不同,则右侧运算对象将换成左侧运算对象的类型。
赋值运算符满足右结合律
赋值运算优先级较低

递增和递减运算符

递增和递减运算符由两种形式
前置版本:先将运算对象加1(减1),然后将改变后的对象作为求值结果。
后置版本:求值结果式运算对象改变前的那个副本。

int i = 0,j;
j = ++i; //j = 1,i = 1
j = i++; //j = 1,i = 2

建议:若非必须,否则不用递增递减运算符的后置版本。

成员运算符

点运算符和箭头运算符都可以用于访问成员。

string s1 = "a string", *p = &s1;
auto n = s1.size();
n = (*p).size();  //*(解引用运算符)的优先级地狱.(成员访问运算符),所以是(*p)
n = p->size();

条件运算符

条件运算符(?:)可以理解成一个简写的if…else
cond?expr1:expr2
条件运算符的优先级非常低,在表达式中嵌套时,一般加上括号。

位运算符《C++ Primer中文版(第五版)》 第四章 表达式_第3张图片

注意事项:
移位运算符(又叫IO运算符)满足左结合律。
位运算符作用于整数类型的运算对象。
一般来说如果运算对象是“小整型”,则他的值会被自动提升成较大的整数类型。
二进制位或者向右或者向左移动,移除边界之外的位就被舍弃掉了。
移位运算符的优先级不高不低,有必要在适当的地方加上括号。
建议:
仅将位运算符用于处理无符号类型。

sizeof运算符

sizeof运算符返回一条表达式或一个类型名字所占的字节数。
注意事项:
在sizeof的运算对象中解引用一个无效的指针仍是一个安全的行为,因为指针并没有被真正使用。
C++11允许我们使用作用域运算符来获取类成员的大小。

std::cout << sizeof(MyClass::num) << std::endl; 
//如果num是私有成员:编译器报错
//test.cpp:24:34: error: 'int MyClass::num' is private within this context
//   std::cout << sizeof(MyClass::num) << std::endl;
//如果是公有成员那么,改行输出4(该成员为int)

对char或者类型为char的表达式执行sizeof得到的结果是1
对引用执行sizeof运算得到被引用对象所占空间的大小。
对指针执行sizeof运算得到指针本上的大小
对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需要有效。
对数组执行sizeof运算得到整个数组所占空间的大小。
对string对象或vector对象执行sizeof只返回该类型固定部分的大小,不会计算对象中元素占用了多少空间。

 int arr[10] = {10};
 std::string str1, str2("hello world");
 std::vector vec, vec1(std::begin(arr), std::end(arr));
 std::cout << "sizeof(str1) = " << sizeof(str1) 
 		   << ", sizeof(str2) = " << sizeof(str2) 
 		   << std::endl; //输出sizeof(str1) = 32,sizeof(str2) = 32
 std::cout << "sizeof(vec) = " << sizeof(vec) 
           << ", sizeof(vec1) = " << sizeof(vec1) 
           << std::endl; //输出sizeof(vec) = 24,sizeof(vec1) = 24

逗号运算符

逗号运算符含有两个运算对象,按照从左往右的顺序依次求值。

类型转换

什么是类型之间的关联?

如果两种类型可以相互转换,那么他们就是关联的。

什么是隐式转换?

类型的转换过程不需要程序员介入,有时甚至不需要程序员了解的类型转换,称作隐式转换。

算术转换

什么是算术转换?

把一种算术类型转换成另外一种算术类型。

什么是整型提升?

把小整数类型转换成较大的整数类型。

其他隐式类型转换

数组转换成指针:
在大多数用到数组的表达式中,数组自动转换成指向数组首元素的指针。

指针转换:
常量整数值0或者字面值nullptr能够转换成任意指针类型
指向常量的指针能够转换成void*
指向任意对象的指针能够转换成const void*
再有继承关系的类型间还有另外一种指针转换的方式。

转换成布尔类型:
存在一种从算术类型或指针类型向布尔类型自动转换的机制。

转换成常量:
允许将指向非常量类型的指针转换成相应的指向相应的常量类型的指针。

类类型定义的转换:
类类型能定义由编译器自动执行的转换,不过编译器每次只能执行一中类类型的转换。

显式转换

什么是显式转换?

程序员通过某种方式完成的类型转换。
命名的强制类型转换
格式:
cast-name(expression)

static_cast

​任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast.
补充说明:
当需要把一个较大的算术类型赋值给较小的类型时,static_cast非常有用。
static_cast对编译器无法自动执行的类型转换也非常有用。

    int i = 10000;
    double j = 1000.0;
    double slope = static_cast(i) / j;
    void *p = &j;
    //double *dp = p; 
    //error: invalid conversion from 'void*' to 'double*' [-fpermissive]
    //double *dp = p;
    double *dp = static_cast(p); 

const_cast

只能改变运算对象的底层const
底层const:

int i = 10;
const int* p  = &i; //这个const是底层const
const int &rp = i;  //这个const是底层const

reinterpret_cast

为运算对象的位模式提供较低层次上的重新解释。
注意事项:
reinterpret_cast本质上依赖于机器,要想安全使用reinterpret_cast必须对涉及的类型和编译器实现转换过程***都非常了解***。 (说得这么吓人,好了,我不用)

旧式的强制类型转换

格式:
type (expr);
(type) expr;

运算符优先级表

《C++ Primer中文版(第五版)》 第四章 表达式_第4张图片

你可能感兴趣的:(C/C++学习笔记,c++,开发语言)