c++博大精深, 越看越觉得不会的太多. 回炉一下吧~ 大神们 请勿浪费时间~
左值:在内存中具有明确的存储地址的的值.
右值: 内从没有明确地址, 随便一个地方存储的数值. 可以被左值代替, 但是不能代替左值.
通常的: 赋值运算符得到的结果是左值, 取地址符返回的指针, 该指针是右值. 内置解引用, 下标运算符的结果都是左值. 内置类型和迭代器的递增递减运算符 也是左值
加减乘除, 小括号 跟数学一样
很多运算符没有执行顺序, 如 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的值
返回一个表达式或类型所占的字节数. 返回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
type 目标类型
expression 需要转换的值
cast-name: static_cast, dynamic_cast, const_cast, reinterpret_const.
static_cast: 只要不包含底层const, 都可以用.如 void*p =&d; double *dp = static_cast
const_cast: 只能改变常量属性, 即只能去掉const性质, 不能更改类型.
reinterpret_cast 为运算对象的位模式提供较低层次上的重新解释. 跟机器有很大关系,最好不用.
dynamic_cast:
原则, 最好不要用显示转换. const_cast只在const和非const之间转换, 不转换类型, 其余情况下用static_cast. 继承类之间转换用dynamic_cast. reinterpret_cast 就让他随风吧~