《C++primer(第五版)》学习之路-第四章:表达式

【 声明:版权所有,转载请标明出处,请勿用于商业用途。  联系信箱:[email protected]】

4.1 基础

1.表达式由一个或多个运算对象(operand)组成,对表达式求值将得到一个结果(result)。字面值和变量是最简单的表达式(expression),其结果就是字面值和变量的值。把一个运算符(operator)和一个或多个运算对象组合起来可以生成较复杂的表达式。

2.C++定义了一元运算符(unary operator)和二元运算符(binary operator)。作用于一个运算对象的运算符是一元运算符,如取地址符(&)和解引用符(*);作用于两个运算对象的运算符是二元运算符,如相等运算符(==)和乘法运算符(*)。除此之外,还有一个作用于三个运算对象的三元运算符。函数调用也是一种特殊的运算符,它对运算对象的数量没有限制。

一些符号既能作为一元运算符也能作为二元运算符。以符号*为例,作为一元运算符时执行解引用操作,作为二元运算符时执行乘法操作。一个符号到底是一元运算符还是二元运算符由它的上下文决定。对于这类符号来说,它的两种用法互不相干,完全可以当成两个不同的符号。

3.在表达式求值的过程中,运算对象常常由一种类型转换成另外一种类型。

4.C++语言定义了运算符作用于内置类型和复合类型的运算对象时所执行的操作。当运算符作用于类类型的运算对象时,用户可以自行定义其含义。因为这种自定义的过程事实上是为已存在的运算符赋予了另外一层含义,所以称之为重载运算符(overloaded operator)。IO库的>>和<<运算符以及string对象、vector对象和迭代器使用的运算符都是重载的运算符。

5.C++的表达式要不然是右值(rvalue,读作"are-value"),要不然就是左值(lvalue,读作"ell-value")。

当一个对象被用作右值的时候,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。

6.表达式最终的值依赖于其子表达式的组合方式。高优先级运算符的运算对象要比低优先级运算符的运算对象更为紧密地组合在一起。如果优先级相同,则其组合规则由结合律确定。

7.以下两条经验准则对书写复合表达式有益:

1.拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求。

2.如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。

4.2 算术运算符

1.

2.运算符%俗称"取余"或"取模"运算符,负责计算两个整数相除所得的余数,参与取余运算的运算对象必须是整数类型。C++11新标准则规定商一律向0取整(即直接切除小数部分)。

除了 m导致溢出的特殊情况,其他时候(-m)/n和m/(-n)都等于-(m/n),m%(-n)等于m%n,(-m)%n等于-(m%n)。

4.3 逻辑和关系运算符

1.

2.对于逻辑与运算符来说,当且仅当左侧运算对象为真时才对右侧运算对象求值。

对于逻辑或运算符来说,当且仅当左侧运算对象为假时才对右侧运算对象求值。

3.进行比较运算时除非比较的对象是布尔类型,否则不要使用布尔字面值true和false作为运算对象。

4.4 赋值运算符

1.每种运算符都有相应的复合赋值形式:

+=      -=      *=      /=      %=  //算术运算符
<<=         >>=         &=      ^=      |=  //位运算符

4.5 递增和递减运算符

1.增运算符(++)和递减运算符(  )为对象的加1和减1操作提供了一种简洁的书写形式。

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

有C语言背景的读者可能对优先使用前置版本递增运算符有所疑问,其实原因非常简单:前置版本的递增运算符避免了不必要的工作,它把值加1后直接返回改变了的运算对象。与之相比,后置版本需要将原始值存储下来以便于返回这个未修改的内容。如果我们不需要修改前的值,那么后置版本的操作就是一种浪费。

对于整数和指针类型来说,编译器可能对这种额外的工作进行一定的优化;但是对于相对复杂的迭代器类型,这种额外的工作就消耗巨大了。建议养成使用前置版本的习惯,这样不仅不需要担心性能的问题,而且更重要的是写出的代码会更符合编程的初衷。

4.6 成员访问运算符

1.点运算符和箭头运算符都可用于访问成员,其中,点运算符获取类对象的一个成员;箭头运算符与点运算符有关,表达式ptr->mem等价于(*ptr).mem。

2.箭头运算符作用于一个指针类型的运算对象,结果是一个左值。点运算符分成两种情况:如果成员所属的对象是左值,那么结果是左值;反之,如果成员所属的对象是右值,那么结果是右值。

4.7 条件运算符

1.条件运算符(? :)允许我们把简单的if-else逻辑嵌入到单个表达式当中,条件运算符按照如下形式使用:

cond ? expr1 : expr2;

其中cond是判断条件的表达式,而expr1和expr2是两个类型相同或可能转为某个公共类型的表达式。条件运算符的执行过程是:首先求cond的值,如果条件为真对expr1求值并返回该值,否则执行expr2。

4.8 位运算符

1.

2.对于位与运算符(&)来说,如果两个运算对象的对应位置都是1则运算结果中该位为1,否则为0。对于位或运算符(|)来说,如果两个运算对象的对应位置至少有一个为1则运算结果中该位为1,否则为0。对于位异或运算符(^)来说,如果两个运算对象的对应位置有且只有一个为1则运算结果中该位为1,否则为0。

4.9 sizeof运算符

1.sizeof运算符返回一条表达式或一个类型名字所占的字节数。sizeof运算符满足右结合律,其所得的值是一个size_t类型的常量表达式。

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

对char或者类型为char的表达式执行sizeof运算,结果得1。

对引用类型执行sizeof运算得到被引用对象所占空间的大小。

对指针执行sizeof运算得到指针本身所占空间的大小。

对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需有效。

对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转换成指针来处理。

对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。

4.10 逗号运算符

1.逗号运算符(comma operator)含有两个运算对象,按照从左向右的顺序依次求值。和逻辑与、逻辑或以及条件运算符一样,逗号运算符也规定了运算对象求值的顺序。

对于逗号运算符来说,首先对左侧的表达式求值,然后将求值结果丢弃掉。逗号运算符真正的结果是右侧表达式的值。如果右侧运算对象是左值,那么最终的求值结果也是左值。

4.11 类型转换

1.在下面这些情况下,编译器会自动地转换运算对象的类型:

在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型。

在条件中,非布尔值转换成布尔类型。

初始化过程中,初始值转换成变量的类型;在赋值语句中,右侧运算对象转换成左侧运算对象的类型。

如果算术运算或关系运算的运算对象有多种类型,需要转换成同一种类型。

函数调用时也会发生类型转换。

2.一个命名的强制类型转换具有如下形式:

cast-name<type>(expression);

其中,type是转换的目标类型而expression是要转换的值。如果type是引用类型,则结果是左值。cast-name是static_cast、dynamic_cast、const_cast和reinterpret_cast中的一种。dynamic_cast支持运行时类型识别。cast-name指定了执行的是哪种转换。

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

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

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

4.12 运算符优先级表

PS:部分练习答案

练习4.6

i % 2 == 0 ? "even" : "odd"

练习4.10

int i = 0;
while(cin >> i && i != 42)

练习4.11

a > b && b > c && c > d

练习4.21

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::vector;

int main()
{
	vector<int> num {1,2,3,4,5,6,7,8,9,10};
	for(auto &c:num)
	{
		if(c%2)
			c = c*2;
	}
	for(auto c:num)
		cout<<c<<endl;
}

练习4.23

string pl = s + (s[s.size() - 1] == 's' ? "" : "s") ;

练习4.25

结果的二进制为1111 1111 1111 1111 1110 0011 1000 0000

十进制表示为-7296

练习4.28

#include <iostream>

using std::cout;
using std::endl;

int main()
{
	cout << "bool\t\tis " << sizeof(bool) << "bytes." << endl;
	cout << "char\t\tis " << sizeof(char) << "bytes." << endl;
	cout << "wchar_t\t\tis " << sizeof(wchar_t) << "bytes." << endl;
	cout << "char16_t\tis " << sizeof(char16_t) << "bytes." << endl;
	cout << "char32_t\tis " << sizeof(char32_t) << "bytes." << endl;
	cout << "short\t\tis " << sizeof(short) << "bytes." << endl;
	cout << "int\t\tis " << sizeof(int) << "bytes." << endl;
	cout << "long\t\tis " << sizeof(long) << "bytes." << endl;
	cout << "long long\tis " << sizeof(long long) << "bytes." << endl;
	cout << "float\t\tis " << sizeof(float) << "bytes." << endl;
	cout << "double\t\tis " << sizeof(double) << "bytes." << endl;
	cout << "long double\tis " << sizeof(long double) << "bytes." << endl;
	cout << endl;

	return 0;
}

练习4.36

i *= static_cast<int>(d);

练习4.37

int i; double d; const string *ps; char *pc; void *pv;
pv = (void*)ps; // pv = const_cast<string*>(ps); or pv = static_cast<void*>(const_cast<string*>(ps));
i = int(*pc);   // i = static_cast<int>(*pc);
pv = &d;        // pv = static_cast<void*>(&d);
pc = (char*)pv; // pc = reinterpret_cast<char*>(pv);

版权声明:本文为博主原创文章,如果转载,请注明出处

时间: 2024-10-04 11:28:58

《C++primer(第五版)》学习之路-第四章:表达式的相关文章

C++ Primer 第五版学习笔记

<C++ Primer>第五版中文版学习笔记 ? C++ Primer 第五版学习笔记

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器

C++ Primer(第五版)学习笔记_9_标准模板库_multimap多重映照容器 多重映照容器multimap与map结构基本相同,但由于重复键值存在,所以multimap的元素插入.删除.查找都与map的方法不相同. 1.multimap对象创建.元素插入 插入元素时,需要使用insert()方法和类似pair<string,double>("Jack", 300.5)的元素结构.可以看到,重复的元素是按照插入的先后顺序排序的. #include <iostre

C++ Primer(第五版)学习笔记_5_标准模板库string(2)

C++ Primer(第五版)学习笔记_5_标准模板库string(2) 10.搜索string对象的元素或子串 采用find()方法可查找字符串中的第一个字符元素(char, 用单引号界定)或者子串(用双引号界定):如果查到,则返回下标值(从0开始计数),如果查不到,则返回一个很大的数string:npos(即:4294967295). #include <iostream> #include <stdio.h> #include <string> using nam

C++ Primer(第五版)学习笔记_3_标准模板库vector(2)

C++ Primer(第五版)学习笔记_3_标准模板库vector(2) 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 6.元素的插入 insert()方法可以在vector对象的任意位置前插入一个新的元素,同时,vector自动扩张一个元素空间,插入位置后的所有元素依次向后挪动一个位置. 要注意的是,insert()方法要求插入的位置,是元素的迭代器位置,而不是元素的下标. #include <iostream> #include <vector> using namespa

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门

C++ Primer(第五版)学习笔记_1_标准模板库--快速入门 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 标准模板库(STL)提供三种类型的组件:容器.迭代器和算法,他们都支持泛型程序设计标准. 容器主要有两类:顺序容器和关联容器.顺序容器(vector.list.deque和string等)是一系列元素的有序集合.关联容器(set.multiset.map和multimap)包含查找元素的键值. 迭代器的作用是遍历容器. STL算法库包含四类算法:排序算法.不可变序算法.变序性算法

C++ Primer(第五版)学习笔记_2_标准模板库vector(1)

C++ Primer(第五版)学习笔记_2_标准模板库vector(1) 欢迎大家阅读参考,如有错误或疑问请留言纠正,谢谢 向量容器vector不但能像数组一样进行随机访问,还能在尾部插入元素,完全可以替代数组. 值得注意的是,vector具有内存自动管理的功能,对于元素的插入和删除,可以动态调整所占的内存空间. 容器vector的下标是从0开始的,如果vector容器的大小是n,则元素下标为0~n-1,这和数组的一样的.不一样的是,vector可以随时调整其大小. vector重要的方法有三个

C++ Primer(第五版)学习笔记_4_标准模板库string(1)

C++ Primer(第五版)学习笔记_4_标准模板库string(1) 1.创建string对象 创建一个空字符串,其长度为0 #include <iostream> #include <string> using namespace std; int main(int argc, char* argv[]) { string s; cout << s.length() << endl; return 0; } 运行结果: 0 2.给string对象赋值

C++ Primer(第五版)学习笔记_6_标准模板库_set集合容器

C++ Primer(第五版)学习笔记_6_标准模板库_set集合容器 Set集合容器实现了红黑树(Red-BlackTree)的平衡二叉检索树的数据结构,在插入元素时,它会自动调整二叉树的排序,把该元素放到适当的位置. (1)确保每个子树根节点的键值大于左子树所有节点的键值,而小于右子树所有节点的键值: (2)另外,还得确保根节点左子树的高度与右子树的高度相等.这样,二叉树的高度最小,从而检索速度最快. 平衡二叉检索树的检索使用中序遍历算法,检索效率高.默认情况下,将键值由小到大遍历. 对于s

C++ Primer(第五版)学习笔记_8_标准模板库_map映照容器

C++ Primer(第五版)学习笔记_8_标准模板库_map映照容器 map映照容器的元素数据是由一个键值和一个映照数据组成的,键值与映照数据之间具有一一映照的关系. map映照容器的数据结构也是采用红黑树来实现的. 1.map创建.元素插入和遍历访问 #include <iostream> #include <stdio.h> #include <vector> #include <map> #include <string> using n

C++ Primer(第五版)学习笔记_7_标准模板库_multiset多重集合容器

C++ Primer(第五版)学习笔记_7_标准模板库_multiset多重集合容器 多重集合容器multiset与set一样,也是使用红黑树来组织元素数据的,唯一不用的是,multiset允许重复的元素键值插入.其结构示意图如下: 1.multiset元素插入 #include <iostream> #include <stdio.h> #include <vector> #include <set> #include <string> usi