C++ 小白 学习记录--4

c++博大精深, 越看越觉得不会的太多. 回炉一下吧~ 大神们 请勿浪费时间~

第四章

1. 基础

左值和右值

左值:在内存中具有明确的存储地址的的值.

右值: 内从没有明确地址, 随便一个地方存储的数值. 可以被左值代替, 但是不能代替左值.

通常的: 赋值运算符得到的结果是左值, 取地址符返回的指针, 该指针是右值.  内置解引用, 下标运算符的结果都是左值. 内置类型和迭代器的递增递减运算符 也是左值

优先级和结合律

加减乘除, 小括号 跟数学一样

C++ 小白 学习记录--4_第1张图片

C++ 小白 学习记录--4_第2张图片

C++ 小白 学习记录--4_第3张图片

C++ 小白 学习记录--4_第4张图片

求值顺序

很多运算符没有执行顺序, 如 int i= f1()* f2(), 不清楚 究竟是先执行的f1还是f2, 同样的还有<<, 尽管vs2019 中 执行int i=0; cout<有明确执行顺序的运算符有 &&, ||,?:, 和逗号.

算数运算符

算数运算符遵从数学的运算规则, 需要注意的是 + - 一元运算符. 一元运算符会返回运算对象的一个副本. 在总用于bool类型时 需要注意的是: bool b=true; bool b2 =-b; 此时b2 是true, 与前面的bool与int转换类似, bool 类型的值 false为0 , 其余值为true, 即使是负值.

一些特殊的地方: 

1. 整数相除, 小数被舍弃

2. % 两边必须是整数

3.  /  商一律向0取整

需要注意 21%-5  = 1  -21 %5  = -1  -21%-5 = 1的区别;

逻辑和关系运算符

运算结果为右值. 短路求值.

三种判断

if(val) // val 不是0时 为真

if(!val) // val 是0 时 为真

if(val == true) // 只有val 是1 时 才为真, 运算时会把true 转化为int的 1

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

赋值运算符

赋值和初始化的区别.

满足右结合律. 如 int i1, i2;  i1=i2=xxx=0;

复合赋值运算符与普通运算符的区别:

如 a+=1  和 a=a+1 右侧表达式 第一次是计算+, 第二次是赋值.

递增和递减 运算符

前置版本 得到递增以后的值, 作为左值返回对象本身

后置版本 得到递增之前的值, 作为右值, 返回原始值的副本

	int i1,i2,j1,j2;
	i1 = i2 = j1 = j2 = 0;
	j1 = ++i1;
	j2 = i2++;
	cout << "i1: " << i1 << " j1: " << j1 << endl; // i1 =1 , j1 =1
	cout << "i2: " << i2 <<" j2: " << j2 << endl; // i2=1, j2 =0

除非必须, 否则尽量不用后置版本, 一种例外:

	auto p = v.begin();
	while (v != v.end() && *p > = 0) {
		cout << *p++ << endl;  // vector 中 输出当前值然后移动到下一个元素
	}

成员访问运算符

ptr->item  等于 (*ptr).item

区别: -> 结果是左值, 点 取决于成员所属的对象

条件运算符

当条件运算符的两个表达式都是左值或者能转成同一种左值, 运算结果是左值, 否则结果是右值.

如果表达式1和表达式2 的结果类型 不能统一, 即使是使用auto 类型 也会报错. 如

	int i1 = 10;
	double b = 2.2;
	string a = "123";
	auto bb = i1 > 1 ? i1 : a; // 报错:表达式必须具有算数或未区分范围的枚举类型, 跟nodejs 不同
	auto aa = i1 > 10 ? i1 : b;
	cout << "aa: " << aa << endl;

位运算符

位运算对于符号位的处理没有明确规定, 所以 不要处理有符号数.

1. 右侧的运算对象一定不能为负

2. 右侧的值必须小于结果的位数

3. 超出边界的位将被丢弃.

4. 小整形数据会被提升成较大的整型类型

位求反运算符

char -> int  高位补零

有些小技巧; 1UL<<27 生成一个只有27位为1的值, ~(1UL<<27) 一个只有27位为0的值

sizeof 运算符

返回一个表达式或类型所占的字节数. 返回size_t类型的常量表达式.

sizeof 不会计算表达式的值, 即使是空指针, 其也能计算指针指向的对象所占用的空间

1. char 结果为1

2. 引用类型 返回被引用对象的大小

3. 指针 返回指针本身所占空间的大小

4. 解引用指针,  返回指针所指对象所占用的空间大小

5. 数组 整个数组所占空间的大小

6. string, vector 只返回固定部分的大小, 不会计算对象中的元素占用了多少空间.

	vector vcs = { "123","456","789" };
	cout << "sizeof vcs: " << sizeof(vcs) << endl; // 16
	vector vcs2 = { "1230","4567","7890" };
	cout << "sizeof vcs2: " << sizeof(vcs2) << endl; // 16
	vector vcs3 = { "1230","4567","7890", "" };
	cout << "sizeof vcs3: " << sizeof(vcs3) << endl; // 16
	vector vcs4;
	cout << "sizeof vcs4: " << sizeof(vcs4) << endl; // 16
	vector vcs5;
	cout << "sizeof vcs5: " << sizeof(vcs5) << endl; // 16
	string s6;
	cout << "sizeof s6: " << sizeof(s6) << endl; // 28
	string s7="hello world";
	cout << "sizeof s7: " << sizeof(s7) << endl; // 28

逗号运算符

多用于连续定义以及for循环中.

类型转换

隐式转换 

算术转换

无符号类型的运算对象

如果一个是无符号类型, 一个是有符号类型, 无符号类型不小于带符号类型, 则带符号类型转化为无符号的. 如果此时带符号的类型恰好是负数, 会带来严重的副作用.

类型转换的一个原则 就是 想上, 即:如果其中一个对象 能容纳另一个对象, 则向该对象转换. 有符号向无符号转换. 如果带符号大于无符号的 则不同的编译器 结果不同.

其他类型的隐式转换:

数组转换为指针(decltype, 取地址符&, sizeof, typeid, 用引用初始化数组)

指针的转换:

常量整数值0, nullptr能转任意指针. 

非常量指针能转void*, 

 

指向对象的指针能转const void*.

空指针转布尔false, 非空都是true

指向非常量类型的指针转指向相应类型的常量指针, 引用也是如此.

显示转换

cast-name(expression);

type 目标类型

expression 需要转换的值

cast-name: static_cast, dynamic_cast, const_cast, reinterpret_const.

    static_cast: 只要不包含底层const, 都可以用.如 void*p =&d; double *dp = static_cast(p);

   const_cast: 只能改变常量属性, 即只能去掉const性质, 不能更改类型.

   reinterpret_cast 为运算对象的位模式提供较低层次上的重新解释. 跟机器有很大关系,最好不用.

   dynamic_cast:

原则, 最好不要用显示转换.  const_cast只在const和非const之间转换, 不转换类型, 其余情况下用static_cast. 继承类之间转换用dynamic_cast. reinterpret_cast 就让他随风吧~

 

你可能感兴趣的:(c++学习,c++)