补第三章
size_t
size_t是一些C/C++标准在stddef.h/cstddef中定义的。这个类型足以用来表示对象的大小。size_t的真实类型与操作系统有关。
在32位架构中被普遍定义为:
typedef unsigned int size_t;
而在64位架构中被定义为:
typedef unsigned long size_t;
size_t在32位架构上是4字节,在64位架构上是8字节,在不同架构上进行编译时需要注意这个问题。而int在不同架构下都是4字节,与size_t不同;且int为带符号数,size_t为无符号数。
为什么有时候不用int,而是用size_type或者size_t:
与int固定四个字节不同有所不同,size_t的取值范围是目标平台下最大可能的数组尺寸,一些平台下size_t的范围小于int的正数范围,又或者大于unsigned int. 使用Int既有可能浪费,又有可能范围不够大。
一、左值和右值
1. C++表达式要不然是左值,要不然是右值:
左值可以位于赋值语句的左侧,右值则不能;
一个左值表达式的求值结果是一个对象或一个函数,然而以常量对象为代表的的某些左值实际上不能作为赋值语句的左值运算对象。
一个简单归纳:当一个对象被用作右值时,用的是对象的值(内容);当对象被用作左值的时候,用的是对象的身份(在内存中的位置)。
2. 使用decltype关键字时,左值右值有不同
如果表达式的求值结果是左值,decltype作用于该表达式得到一个引用类型。
例如:若p的类型是int*, 因为解引用运算符生成左值,所以decltype(*p)的结果是int &;
取地址运算符生成右值,故decltype(&p)的结果是int**;
二、优先级和结合律和运算符
1. 括号无视优先级和结合律
2. 运算符优先级顺序表(复制的)
优先级 | 运算符 | 结合律 | 助记 |
---|
1 | :: | 从左至右 | 作用域 |
2 | a++ 、a-- 、
type() 、type{} 、
a() 、a[] 、
. 、-> | 从左至右 | 后缀自增减、 函数风格转型、 函数调用、下标、 成员访问 |
3 | ! 、~ 、
++a 、--a 、+a 、-a 、
(type) 、sizeof 、&a 、
*a 、
new 、 new[] 、delete 、 delete[] | 从右至左 | 逻辑非、按位非、 前缀自增减、正负、 C 风格转型、取大小、取址、 指针访问、 动态内存分配 |
4 | .* 、->* | 从左至右 | 指向成员指针 |
5 | a*b 、a/b 、a%b | 从左至右 | 乘除、取模 |
6 | a+b 、a-b | 从左至右 | 加减 |
7 | << 、>> | 从左至右 | 按位左右移 |
8 | < 、<= 、> 、>= | 从左至右 | 大小比较 |
9 | == 、!= | 从左至右 | 等价比较 |
10 | a&b | 从左至右 | 按位与 |
11 | ^ | 从左至右 | 按位异或 |
12 | ` | ` | 从左至右 |
13 | && | 从左至右 | 逻辑与 |
14 | ` | | ` |
15 | a?b:c 、
= 、+= 、-= 、*= 、/= 、%= 、&= 、^= 、` | =、 <<=、 >>=` | 从右至左 |
16 | , | 从左至右 | 逗号 |
3. 对书写复合表达式有益的两条经验
a)拿不准的时候最好用括号来强制让表达式的组合关系符合程序逻辑的要求;
b)如果改变了某个运算对象的值,在表达式的其他地方不要再使用这个运算对象。
(当改变运算对象的子表达式本身就是另外一个子表达式的运算对象时该规则无效)
4. 递增和递减运算符
除非必须,否则不使用递增递减运算符的后置版本(即i++)
优先使用前置版本(++i)
前置和后置的区别:
前置递增运算符:将对象本身作为一个值返回。
后置递增运算符:将对象的原始值的副本作为一个值返回。
5. 成员访问运算符
ptr->mem 等价于 (*ptr).mem
6. 条件运算符(优先级很低,嵌套时最好带上括号)
cond ? expr1:expr2
cond为真执行expr1; 为假执行expr2。
当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果是左值,否则是右值。(为了可读性,最好不要超过两到三层)
7. 位运算符&移位运算符&位求反运算符
作用于整数类型的运算对象,并把运算对象看成是二进制位的集合。
提供检查和设置二进制位的功能。
关于符号位没有明确的规定,所以建议仅将位运算符用于处理无符号类型。
& | 按位与操作,按二进制位进行"与"运算。运算规则:
0&0=0;
0&1=0;
1&0=0;
1&1=1; | (A & B) 将得到 12,即为 0000 1100 |
| | 按位或运算符,按二进制位进行"或"运算。运算规则:
0|0=0;
0|1=1;
1|0=1;
1|1=1; | (A | B) 将得到 61,即为 0011 1101 |
^ | 异或运算符,按二进制位进行"异或"运算。运算规则:
0^0=0;
0^1=1;
1^0=1;
1^1=0; | (A ^ B) 将得到 49,即为 0011 0001 |
~ | 取反运算符,按二进制位进行"取反"运算。运算规则:
~1=-2;
~0=-1; | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 |
<< | 二进制左移运算符。将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。 | A << 2 将得到 240,即为 1111 0000 |
>> | 二进制右移运算符。将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。 | A >> 2 将得到 15,即为 0000 1111 |
8. sizeof运算符
返回一条表达式或一个类型名字所占的字节数。
满足右结合律,所得的值是一个size_t类型的常量表达式。
两种形式:
sizeof (type)
sizeof expr //返回表达式结果类型的大小,并不实际计算其运算对象的值
一些例子
Sales_data data, *p;
sizeof(Sales_data); //Sales_data类型的对象所占的空间大小
sizeof data; //data类型的大小,结果如上
sizeof p; //指针所占的空间大小
sizeof *p; //指针所指的类型所占的空间大小,即sizeof(Sales_data)
sizeof data.revenue; //Sales_data的成员reveune对应类型的大小
sizeof Sales_data::revenue; //Sales_data的成员reveune对应类型的大小
sizeof 运算符的结果部分地依赖于其作用的类型:
- 对char或者类型为char的表达式执行sizeof运算符,结果得1。
- 对引用类型执行sizeof运算得到被引用对象所占空间的大小。
- 对指针执行sizeof运算得到指针本身所占空间的大小。
- 对解引用指针执行sizeof运算得到指针指向的对象所占空间的大小,指针不需要有效。
- 对数组执行sizeof运算得到整个数组所占空间的大小,等价于对数组中所有的元素各执行一次sizeof运算并将所得结果求和。注意,sizeof运算不会把数组转换成指针来处理。
- 对string对象或vector对象执行sizeof运算只返回该类型固定部分的大小,不会计算对象中的元素占用了多少空间。
因为执行sizeof运算可以得到整个数组的大小,则可以用数组的大小初一单个元素的大小得到数组中元素的值。
9. 逗号运算符
优先级最低,实例
int somevalue = 1;
cout<<(somevalue?++x,++y:--x,--y);
//这里输出的是y的值,无论somevalue判断后是真还是假,都输出逗号右边的值;
cout<<("idhiw","voenivef");
//加了括号的话就是输出最右边的表达式,没加括号就不行,因为逗号运算符的优先级最低。
三、类型转换
1. 相互转换(相互关联)
如果两种类型有关联,那么当程序需要其中一种类型的运算对象时,可以用另一种关联类型的对象或值来替代。即若两个类型可以相互转换的话,则称它们是相互关联的。
2. 隐式转换
何时发生:
- 在大多数表达式中,比int类型小的整型值首先提升为较大的整数类型
- 在条件中,非bool值转化为bool值
- 初始化过程中,初始值转换成变量的类型,在赋值语句中,右侧运算对象转换成左侧运算对象的类型
- 如果算术关系或运算关系的对象有多种类型,需要换换成同一种类型
- 函数调用也会发生类型转换
3. 算术转换C/C++ 算术转换规则 (以及容易踩的坑)_茶佬牛逼-CSDN博客算术转换指的是运算符的两个操作数类型不同时所发生的转换,不要小看这个点,可能会造成灾难性的程序问题。首先需要明确两个概念,后面要介绍的转换规则会依赖于这两个概念:一、整型提升https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_promotion算术运算符不接受小于int类型的变量,所以会存在以下的整型提升:signed char、signed short:转换为intunsigned charhttps://blog.csdn.net/yuejisuo1948/article/details/121666396
把一种算术类型转换成另外一种算术类型。
整型提升:负责把小整数类型转换为较大的整数类型。
无符号类型的运算对象:如果某个运算符的运算对象不一致,这些运算对象将转换恒同一种类型。但是如果某个运算对象的类型是无符号类型,那么转换的结果就要依赖于机器中各个整数类型的相对大小了。
如果一个运算对象是无符号类型,另外一个运算对象是带符号类型,而且其中的无符号类型不小于带符号类型,那么带符号的运算对象就转化成无符号的。
四、try语句块和异常处理
1. 典型的异常:失去数据库连接、遇到意外输入等。
bad_alloc: 请求分配内存失败, operator new 或者 operator new []
bad_exception: 函数异常,通常是函数运行错误,抛出的异常
bad_typeid: 类型异常,通常用typeid操作符,作用于一个NULL指针时,而该指针是带有虚函数的类,这时抛出bad_typeid异常
bad_cast: 转换异常,使用dynamic_cast转换引用失败的时候
2. 异常处理机制
a)异常检测
throw表达式:异常检测部分使用throw表达式来表示它遇到了无法处理的问题。我们说throw印发了异常。
if(a != b)
throw runtime_error("Data must refer to same value");
b)异常处理
try语句块:try以关键字try开始,并以一个或多个catch子句结束。try语句块中抛出的异常通常会被某个catch字句(也被称为异常处理代码)处理。
int a, b;
while(cin >> a >> b){
try{
if(a != b)
throw runtime_error("Data must refer to same value");
}catch(runtime_error err){
cout << err.what() << "\nTry Again? Enter y or n" << endl;
char c;
cin >> c;
if(!cin || c == 'n') break;
}
}
上面的代码中,try语句块中如果判断a与b不相等,就抛出一个runtime_error类型的异常,try语句块后有一个与之相对应的catch语句,那么就会执行该catch语句来处理异常。
what函数返回值是一个指向C风格字符串的const char*。what函数返回的C风格字符串的内容与异常对象的类型有关。如果异常类型有一个字符串初始值,则what返回该字符串。对于其他吴初始值的异常类型来说,what返回的内容由编译器决定。
3. 标准异常
a)exception头文件:定义了最通用的异常类exception。它只报告异常的发生,不提供任何额外信息。
b)stdexcept头文件:定义了几种常用的异常类。
logic_error
Runtime error exception
c)new头文件:定义了bad_alloc异常类型。
d)type_info头文件:定义了bad_cast异常类型。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)