C++ Primer 学习笔记(第四章:表达式)

##C++ Primer 学习笔记(第四章:表达式)express


[TOC]数组


###4.1 基础安全

  1. 左值和右值:
    当一个对象被用做右值的时候,用的是对象的值(内容);当对象被用做左值的时候,用的是对象的身份(在内存中的位置)。 一个重要的原则是在须要右值的地方能够用左值来代替,可是不能把右值当成左值(也就是位置)使用。当一个左值被当成右值使用时,实际使用的是它的内容(值)。函数

  2. 使用decltype的时候,若是表达式的求值结果是左值,decltype做用于该表达式获得一个引用类型。 p的类型是int*,而*p会生成左值,多以decltype(*p)会获得引用int &。 由于取地址会生成右值,因此decltype(&p)的结果是int**post

  3. 对于那些没有指定执行顺序的运算符来讲,若是表达式指向并修改了同一个对象(或者执行了IO任务),将会引起错误并产生未定义的行为。例如:学习

int i = 0;
cout << i << " " << ++i << endl;

有4种运算符明确规定了运算对象的求值顺序:逻辑与&&(先求左侧,左侧为真再求右侧)、逻辑或||、条件运算符?:、逗号运算符,ui


###4.2 算术运算符.net

  1. 整数相除的结果仍是整数,若是有小数部分直接弃除。 结果为负的商统一贯0取整,即直接切除小数部分。指针

  2. 参与取模运算%的运算对象必须是整数。 表达式(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 逻辑关系和运算符

  1. 短路求值: 逻辑与(&&):当且仅当左侧运算为真时才对右侧进行求值; 逻辑或(||):当且仅当左侧运算为假时才对右侧进行求值。

  2. 进行比较运算时除非比较的对象时布尔类型,不然不要使用布尔字面值true

if (val == true) {/*...*/} //只有当val等于1时条件才为真

###4.4 赋值运算符

  1. 若是赋值运算符的左右两个运算对象类型不一样,则右侧运算对象将转换成左侧运算对象的类型。
int k = 3.14159;
  1. C++11容许使用花括号括起来的初始值列表做为赋值语句右侧对象。
k = {3.14};
vector<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  1. 把赋值语句放到条件中的用处:(赋值语句的优先级比较低)
while ((i = get_value()) != 42){
	//其余处理
}

###4.5 递增和递减运算符

  1. 关于前置和后置++运算符。 除非必须,不然不用递增递减运算符的后置版本。(由于对于复杂的迭代器类型,后置须要将原始值存储下来,可能涉及到额外的消耗。)

  2. 混用解引用和递增运算符。

auto pbeg = v.begin();
while (pbeg != v.end() && *pbeg >= 0)
	 cout << *pbeg++ << endl;

后置++的优先级高于解引用运算符,表示获得初始值的副本再递增。 这种*pbeg++的写法更简洁。


###4.6 成员访问运算符

  1. 表达式ptr->mem等价于(*ptr).mem

  2. 解引用运算符*优先级低于点运算符,因此执行解引用运算的子表达式两端必须加上括号。(*p).size()


###4.7 条件运算符(?:

  1. cond ? expr1 : expr2,条件运算符只对expr1expr2中的一个求值。expr1expr2的类型应该相同或者能转换成同一种类型。

  2. 条件运算符的优先级很是低,所以当一条长表达式中嵌套了条件运算子表达式时,一般须要在它两端加上括号,尤为是在输出表达式中,不然将产生意想不到的效果。

cout << ((grade < 60) ? "fail" : "pass") << endl;

###4.8 位运算符

  1. 位运算符做用于整数类型的运算对象,并把运算对象当作是二进制位的集合。

  2. 若是运算对象是“小整型”,它的值会被自动提高成较大的整数类型。

  3. 移位运算符>> <<右侧的运算对象必定不能为负,并且值必须严格小于结果的位数,不然将产生未定义的行为。

  4. 利用位运算符能够检测位:

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运算符

  1. sizeof运算符返回一条表达式或一个类型名字所占的字节数,其所得的值是一个size_t类型的常量表达式。当返回一个表达式的值时,sizeof expr并不实际计算其运算对象的值。
int i = 0;
cout << sizeof( i++ ) << endl;//4
cout << i <<endl;//0
  1. sizeof *p:由于sizeof不会实际求运算对象的值,因此即便p是一个无效的(即未初始化)指针也不会有什么影响。在sizeof的运算对象中解引用一个无效指针仍然是一种安全的行为,由于指针实际上并无被真正利用。

  2. sizeof Sales_data::revenue:C++11容许使用做用域运算符来获取类成员的大小。sizeof运算符无须咱们提供一个具体的对象就能访问到类成员,由于要想知道类成员的大小无须真正的获取该成员。

  3. sizeof运算符的结果: char类型或类型为char的表达式 引用类型获得被引用对象所占空间的大小 指针类型获得指针自己所占空间大小 对数组执行获得整个数组所占空间大小,等价于全部元素各执行一次,并将结果求和。(而不会把数组当成指针处理) 对string对象或vector对象执行只返回类型固定部分的大小,不会计算对象中的元素占用了多少空间。

vector<int> v{0};
cout << sizeof v << endl;//12
  1. 返回数组元素的数量:
constexpr size_t sz = sizeof(ia) / sizeof(*ia);
int arr2[sz];//正确,sizeof返回一个常量表达式。

###4.10 逗号运算符

  1. 逗号运算符含有两个运算对象,按照从左向右的顺序依次求值,先对左侧的表达式求值,而后将求值结果丢弃掉,逗号运算符真正的结果是右侧表达式的值。
int i = 1, j = 100;
cout << (i++, j++) << endl;
  1. 逗号运算符的优先级在最末位。

###4.11 类型转换

  1. 算术转换:运算对象将转换成最宽的类型;当表达式中既有浮点型也有整数类型时,整数值将转换成浮点类型。

  2. 数组转换成指针:在大多数用到数组的表达式中,数组自动转换成转向数组首元素的指针。当数组用做decltype关键字参数,或者做为取地址符(&)、sizeoftypeid等运算符的运算对象时,上述转换不会发生。

  3. 常量整数值0或者字面值nullptr能转换成任意指针类型;指向任意很是量的指针能转换成void*;指向任意对象的指针能转换成const void*

  4. 转换成常量:能将指向T的指针或引用分别转换成指向const T的指针或引用。但相反的转换不存在。

int i;
const int &j = i;
const int *p = &i;
int &r = j, *q = p;//错误,不容许const转换成常量
  1. 类类型转换:
string s, t = "a value";//字符串字面值转换成string类型
while (cin >> s)//while的条件部分把cin转换成bool值
  1. 强制类型转换: cast-name<type>(expression):其中type表示转换的目标类型,expression是要转换的值。若是type是引用类型,则结果是左值。cast-name指定了执行的是那种转换,包括static_castdynamic_castconst_castreinterpret_cast中的一种。

  2. static_cast:具备明肯定义的类型转换,只要不包含底层const,均可以使用static_cast

double slope = static_cast<double>(j) / i;

当须要把一个较大的算术类型赋值给较小的类型时很是有用,此时意味着告诉编译器不在意潜在的精度损失,而且不会产生警告。对于编译器没法自动执行的类型转换也颇有用,例如能够用来找回存在于void*指针中的值:(但应当确保转换类型必定正确)

void *p = &d;//任何很是量对象的地址都能存入void*
double *dp = static_cast<double*>(p);
  1. 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)

  1. reinterpret_cast:为运算对象的位模式提供较低层次上的从新解释。
int *ip;
char *pc = reinterpret_cast<char*>(ip);//pc所指的真实对象是一个int而非字符,若是把pc当成普通的字符指针使用就可能在运行时发生错误,例如string str(pc)

使用reinterpret_cast很是危险。使用一个int的地址初始化pc,显示地声称这种转换合法,编译器不会报错,但继续使用将引起糟糕的后果。

  1. dynamic_cast支持运行时类型识别,19.2节介绍。

  2. 强烈建议避免使用强制类型转换,除了重载函数上下文使用const_cast(6.4节)无可厚非,其余状况都应反复斟酌。

  3. 旧式强制类型转换:

type (expr);//函数形式
(type) expr;//C语言风格

它们与上述三种强制转换有类似行为。

相关文章
相关标签/搜索