##C++ Primer 学习笔记(第四章:表达式)express
[TOC]数组
###4.1 基础安全
左值和右值:
当一个对象被用做右值的时候,用的是对象的值(内容);当对象被用做左值的时候,用的是对象的身份(在内存中的位置)。 一个重要的原则是在须要右值的地方能够用左值来代替,可是不能把右值当成左值(也就是位置)使用。当一个左值被当成右值使用时,实际使用的是它的内容(值)。函数
使用decltype
的时候,若是表达式的求值结果是左值,decltype
做用于该表达式获得一个引用类型。 p
的类型是int*
,而*p
会生成左值,多以decltype(*p)
会获得引用int &
。 由于取地址会生成右值,因此decltype(&p)
的结果是int**
。post
对于那些没有指定执行顺序的运算符来讲,若是表达式指向并修改了同一个对象(或者执行了IO任务),将会引起错误并产生未定义的行为。例如:学习
int i = 0; cout << i << " " << ++i << endl;
有4种运算符明确规定了运算对象的求值顺序:逻辑与&&
(先求左侧,左侧为真再求右侧)、逻辑或||
、条件运算符?:
、逗号运算符,
。ui
###4.2 算术运算符.net
整数相除的结果仍是整数,若是有小数部分直接弃除。 结果为负的商统一贯0取整,即直接切除小数部分。指针
参与取模运算%
的运算对象必须是整数。 表达式(m/n)*n+m%n
的结果与m
相同,即m%n
若是不等于0,则它的符号应该和m相同。code
-21 % -8; // -5 -21 / -8; //2 21 % -5; //1 21/ -5; //-4
###4.3 逻辑关系和运算符
短路求值: 逻辑与(&&
):当且仅当左侧运算为真时才对右侧进行求值; 逻辑或(||
):当且仅当左侧运算为假时才对右侧进行求值。
进行比较运算时除非比较的对象时布尔类型,不然不要使用布尔字面值true
if (val == true) {/*...*/} //只有当val等于1时条件才为真
###4.4 赋值运算符
int k = 3.14159;
k = {3.14}; vector<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
while ((i = get_value()) != 42){ //其余处理 }
###4.5 递增和递减运算符
关于前置和后置++
运算符。 除非必须,不然不用递增递减运算符的后置版本。(由于对于复杂的迭代器类型,后置须要将原始值存储下来,可能涉及到额外的消耗。)
混用解引用和递增运算符。
auto pbeg = v.begin(); while (pbeg != v.end() && *pbeg >= 0) cout << *pbeg++ << endl;
后置++
的优先级高于解引用运算符,表示获得初始值的副本再递增。 这种*pbeg++
的写法更简洁。
###4.6 成员访问运算符
表达式ptr->mem
等价于(*ptr).mem
。
解引用运算符*
优先级低于点运算符,因此执行解引用运算的子表达式两端必须加上括号。(*p).size()
。
###4.7 条件运算符(?:
)
cond ? expr1 : expr2
,条件运算符只对expr1
和expr2
中的一个求值。expr1
和expr2
的类型应该相同或者能转换成同一种类型。
条件运算符的优先级很是低,所以当一条长表达式中嵌套了条件运算子表达式时,一般须要在它两端加上括号,尤为是在输出表达式中,不然将产生意想不到的效果。
cout << ((grade < 60) ? "fail" : "pass") << endl;
###4.8 位运算符
位运算符做用于整数类型的运算对象,并把运算对象当作是二进制位的集合。
若是运算对象是“小整型”,它的值会被自动提高成较大的整数类型。
移位运算符>> <<
右侧的运算对象必定不能为负,并且值必须严格小于结果的位数,不然将产生未定义的行为。
利用位运算符能够检测位:
unsigned long quiz1 = 0;//32位 quiz1 |= 1 UL << 27;//表示将第27位置成1 quiz1 &= ~(1 UL << 27);//表示将第27位置成0 bool status = quiz1 & (1 UL << 27);//检测第27位
###4.9 sizeof运算符
size_t
类型的常量表达式。当返回一个表达式的值时,sizeof expr
并不实际计算其运算对象的值。int i = 0; cout << sizeof( i++ ) << endl;//4 cout << i <<endl;//0
sizeof *p
:由于sizeof不会实际求运算对象的值,因此即便p是一个无效的(即未初始化)指针也不会有什么影响。在sizeof
的运算对象中解引用一个无效指针仍然是一种安全的行为,由于指针实际上并无被真正利用。
sizeof Sales_data::revenue
:C++11容许使用做用域运算符来获取类成员的大小。sizeof
运算符无须咱们提供一个具体的对象就能访问到类成员,由于要想知道类成员的大小无须真正的获取该成员。
sizeof
运算符的结果: char
类型或类型为char
的表达式 引用类型获得被引用对象所占空间的大小 指针类型获得指针自己所占空间大小 对数组执行获得整个数组所占空间大小,等价于全部元素各执行一次,并将结果求和。(而不会把数组当成指针处理) 对string
对象或vector
对象执行只返回类型固定部分的大小,不会计算对象中的元素占用了多少空间。
vector<int> v{0}; cout << sizeof v << endl;//12
constexpr size_t sz = sizeof(ia) / sizeof(*ia); int arr2[sz];//正确,sizeof返回一个常量表达式。
###4.10 逗号运算符
int i = 1, j = 100; cout << (i++, j++) << endl;
###4.11 类型转换
算术转换:运算对象将转换成最宽的类型;当表达式中既有浮点型也有整数类型时,整数值将转换成浮点类型。
数组转换成指针:在大多数用到数组的表达式中,数组自动转换成转向数组首元素的指针。当数组用做decltype
关键字参数,或者做为取地址符(&
)、sizeof
及typeid
等运算符的运算对象时,上述转换不会发生。
常量整数值0
或者字面值nullptr
能转换成任意指针类型;指向任意很是量的指针能转换成void*
;指向任意对象的指针能转换成const void*
转换成常量:能将指向T
的指针或引用分别转换成指向const T
的指针或引用。但相反的转换不存在。
int i; const int &j = i; const int *p = &i; int &r = j, *q = p;//错误,不容许const转换成常量
string s, t = "a value";//字符串字面值转换成string类型 while (cin >> s)//while的条件部分把cin转换成bool值
强制类型转换: cast-name<type>(expression)
:其中type
表示转换的目标类型,expression
是要转换的值。若是type
是引用类型,则结果是左值。cast-name
指定了执行的是那种转换,包括static_cast
、dynamic_cast
、const_cast
和reinterpret_cast
中的一种。
static_cast
:具备明肯定义的类型转换,只要不包含底层const
,均可以使用static_cast
。
double slope = static_cast<double>(j) / i;
当须要把一个较大的算术类型赋值给较小的类型时很是有用,此时意味着告诉编译器不在意潜在的精度损失,而且不会产生警告。对于编译器没法自动执行的类型转换也颇有用,例如能够用来找回存在于void*
指针中的值:(但应当确保转换类型必定正确)
void *p = &d;//任何很是量对象的地址都能存入void* double *dp = static_cast<double*>(p);
const_cast
:只能改变运算对象的底层const
(常量指针或声明引用的const
),称为去掉const
行为;它只能改变常量属性,不能改变常量类型。const char *pc; char *q1 = const_cast<char*>(pc);//正确,可是经过p写值是未定义行为 char *q2 = static_cast<char*>(pc);//错误,static_cast不能转换掉const性质 string s1 = const_cast<string>(pc);//错误,const_cast只转换属性 string s2 = static_cast<string>(pc);//正确,字符串字面值转换为string类型
const_cast
经常使用于有函数重载(6.4节)的上下文中。
const_cast
的用法不是将一个const变量变成非const
变量的。看一下标准就能够知道,使用const_cast
把一个本来是const的变量转换为非const
,这是一个未定义行为,是很是很是危险的举动。const_cast
的目的,在于某些变量本来不是const
的,但因为某种特殊缘由,无心间被变成了const
的,例如使用了一个const
引用指向了一个原本不是const
的对象。结果写了一些代码以后发现它实际上须要被修改。 这在平时的工做中不会遇到由于你能够直接把const
引用修改为非const
的,但C++
中可能的状况太多,尤为考虑到不少复用的时候,有时仍是会出现本不应是const
的对象被const
引用了这种状况。尤为是使用模板,比较复杂的状况。这才是const_cast的意义所在。(摘自:求助const_cast的问题(CSDN))
reinterpret_cast
:为运算对象的位模式提供较低层次上的从新解释。int *ip; char *pc = reinterpret_cast<char*>(ip);//pc所指的真实对象是一个int而非字符,若是把pc当成普通的字符指针使用就可能在运行时发生错误,例如string str(pc)
使用reinterpret_cast很是危险。使用一个int
的地址初始化pc
,显示地声称这种转换合法,编译器不会报错,但继续使用将引起糟糕的后果。
dynamic_cast
支持运行时类型识别,19.2节介绍。
强烈建议避免使用强制类型转换,除了重载函数上下文使用const_cast
(6.4节)无可厚非,其余状况都应反复斟酌。
旧式强制类型转换:
type (expr);//函数形式 (type) expr;//C语言风格
它们与上述三种强制转换有类似行为。