1.表达式是c++中最小的计算单元。主要讲的是操作符:定义操作数为内置类型时,这些操作符的含义。支持操作符重载,允许程序员自定义用于类类型时操作符的含义
算术操作符:
2.如果两个操作数为正,除法(/)和求模(%)操作的结果也是正数(或零);如果两个操作数都是负数,除法操作的结果为正数(或零),而求模操作的结果则为负数(或零);如果只有一个操作数为负数,这两种操作的结果取决于机器;求模结果的符号也取决于机器,而除法操作的值则是负数(或零)21 % 6; // ok: result is 3
21 % 7; // ok: result is 0
-21 % -8; // ok: result is -5
21 % -5; // machine-dependent: result is 1 or -4
21 / 6; // ok: result is 3
21 / 7; // ok: result is 3
-21 / -8; // ok: result is 2
21 / -5; // machine-dependent: result -4 or –5
当只有一个操作数为负数时,求模操作结果值的符号可依据分子(被除数)或分母(除数)的符号而定。如果求模的结果随分子的符号,则除出来的值向零一侧取整;如果求模与分母的符号匹配,则除出来的值向负无穷一侧取整。
这里该怎么理解:结果随分子,则除数的绝对值与商的绝对值的乘积接近分子的绝对值,而反之,则是超过分子的绝对值
vs下取决于分子逻辑操作符和关系操作符作用于算术类型和指针类型的操作数
位操作符使用整型和bieset类型的操作数。
1.如果操作数为负数,则位操作符如何处理其操作数的符号位依赖于机器,建议使用unsigned整型
2.对纯粹以使用位操作实现某种目的时,要用unsigned型,但是对signed型bitset也能处理,最终bitset类型数据中存储的是负数的补码?(额外复习下面的知识---计算机中定点数是以补码形式存储的:计算机中的数除了整数之外,还有小数。如何确定小数点的位置呢?通常有两种方法:一种是规定小数点位置固定不变,称为定点数;另一种是小数点的位置不固定,可以浮动,称为浮点数。在计算机中,通常是用定点数来表示整数和纯小数,分别称为定点整数和定点小数。对于既有整数部分、又有小数部分的数,一般用浮点数表示。
在计算机内,定点数有3种表示法:原码、反码和补码。所谓原码就是前面所介绍的二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。 反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
对负数来说:原码到补码的计算方法就是先取反(符号位不变),最后位加一;补码到原码:按照求负数补码的逆过程,数值部分应是最低位减1,然后取反。但是对二进制数来说,先减1后取反和先取反后加1得到的结果是一样的,故仍可采用取反加1 有方法。
正定点数的原码与反码补码是一样的。但是对负定点数提出原码与补码的概念是因为:
计算机的运算部件与寄存器都有一定字长的限制(假设字长为8),因此它的运算也是一种模运算。当计数器计满8位也就是256个数后会产生溢出,又从头开始计数。模的系统中,减法问题可由补数转化成加法问题了(注:计算机的硬件结构中只有加法器,所以大部分的运算都必须最终转换为加法),所以将负数转换位补码表示就可以对减法问题转换为加法3.考虑一个问题,如何获得一个数的二进制,八进制和十六进制。如果输出的话,使用cout的自身函数就可以实现输出八进制和十六进制。可以使用bitset来输出二进制补码,对负数时补码这点要注意。所以程序内部要进行不同进制的处理时,自己写函数解决?
4.左移操作符(<<)在右边插入 0 以补充空位。对于右移操作符(>>),如果其操作数是无符号数,则从左边开始插入 0;如果操作数是有符号数,则插入符号位的副本或者 0 值,如何选择需依据具体的实现而定(vs是加入符号位的副本):::注意计算机存储数据是补码
注意bool值时非零值为真,所以unsigned long int_quiz1 = 0;int_quiz1 |= 1UL<<27;bool status = int_quiz1 & (1UL<<27); 是两个unsigned long 的数据在进行位操作,status只用在int_quizl的27位是1时才会非零即为真
另一个例子
unsigned long ul1 = 3, ul2 = 7;What is the result of each of the following expressions?
下列表达式的结果是什么?
(a) ul1 & ul2 (c) ul1 | ul2
(b) ul1 && ul2 (d) ul1 || ul2
a与c是位操作,bd是逻辑操作赋值操作符的优先级最低,低于关系操作符等
自增和自减操作符:只有在必要的时候使用后自增操作符。对int型对像和指针,编译器优化掉保存操作数原值的额外工作,但对其他复杂的迭代器类型,这种额外的工作将可能花费更大的代价
解引用操作符的优先级低于自增自减操作符
一个例子对指针存储的是同类型对象的地址的理解要加深
vector<string*> psvec;
string temp;
cout<<"enter string"<<endl;
while(cin>>temp)
{psvec.push_back(&temp);/*这种写法自以为很简洁,但是犯了一个严重的错误,vector所有元素存储的都是
temp的地址,而temp的变化是地址不变只是地址中存储的内容变化,所以,就造成每一次存储的地址都一致!!!前一次输入的string会被后一次的string冲掉,对指针的处理要慎重*/}for(vector<string*>::iterator itr=psvec.begin();itr!=psvec.end();++itr){cout<<**itr<<" "<<(*itr)->size()<<endl;
}//修改如下:
vector<string*> psvec;
string temp;
cout<<"enter string"<<endl;
while(cin>>temp)
{string *pstemp=new string;*pstemp=temp;psvec.push_back(pstemp);}for(vector<string*>::iterator itr=psvec.begin();itr!=psvec.end();++itr){cout<<**itr<<" "<<(*itr)->size()<<endl;
delete *itr;
}条件操作符:可以使用一组嵌套的条件操作符求出三个变量的最大值,并将最大值赋给 max:
int max = i > j
? i > k ? i : k
: j > k ? j : k;条件操作符优先级相当低,但是比赋值操作符高
cout << (i < j ? i : j); // ok: prints larger of i and j
cout << (i < j) ? i : j; // prints 1 or 0!
cout << i < j ? i : j; // error: compares cout to int
第二个等效于:
cout << (i < j); // prints 1 or 0
cout ? i : j; // test cout and then evaluate i or j
// depending on whether cout evaluates to true or false
sizeof 操作符的作用是返回一个对象或类型名的长度,返回值的类型为 size_t,长度的单位是字节.
将 sizeof 应用在表达式 expr 上,将获得该表达式的结果的类型长度
sizeof (type name);
sizeof (expr);
sizeof expr;
对引用类型做 sizeof 操作将返回存放此引用类型对象所需的内在空间大小。
对指针做 sizeof 操作将返回存放指针所需的内在大小;注意,如果要获取该指针所指向对象的大小,则必须对指针进行解引用。char *t="hello";cout<<sizeof(t)<<endl;//4字节cout<<sizeof(*t)<<endl;//1字节(char类型的大小)对数组做 sizeof 操作等效于将对其元素类型做 sizeof 操作的结果乘上数组元素的个数。
操作符的优先级:一元操作符>二元操作符.
求值顺序(以前竟没有概念):
C++中,在一个表达式中规定了操作数计算顺序的操作符:&& 和 || 操作符计算其操作数的次序:当且仅当其右操作数确实影响了整个表达式的值时,才计算这两个操作符的右操作数;条件(?:)和逗号操作符. 除此之外,其他操作符并未指定其操作数的求值顺序.
f1() * f2();在做乘法操作之前,必须调用 f1 函数和 f2 函数,毕竟其调用结果要相乘。然而,我们却无法知道到底是先调用 f1 还是先调用 f2。
因为上述原因,如果一个子表达式修改了另一个子表达式的操作数,则操作数的求解次序就变得相当重要:
if (ia[index++] < ia[index])
//编译器可以用下面两种方式之一求该表达式的值:
if (ia[0] < ia[0]) // execution if rhs is evaluated firstif (ia[0] < ia[1]) // execution if lhs is evaluated firstvs中式第一种情况.//改写
if (ia[index] < ia[index + 1]) {
// do whatever
}++index;定义变量时,必须指定其数据类型和名字.而动态创建对象时,只需指定其数据类型,而不必为该对象命名。取而代之的是,new表达式返回指向新创建对像的指针,我们通过指针来访问对象。int i; // named, uninitialized int variableint *pi = new int; // pi points to dynamically allocated,// unnamed, uninitialized int
动态创建的对象可用初始化变量的方式实现初始化:
如果不提供显式初始化,动态创建的对象与在函数内定义的变量初始化方式相同。对于类类型的对象,用该类的默认构造函数初始化;而内置类型的对象则无初始化。
同样也可对动态创建的对象做值初始化:在类型名后面使用一对内容为空的圆括号对动态创建的对象做值初始化
int i(1024); // value of i is 1024int *pi = new int(1024); // object to which pi points is 1024string s(10, '9'); // value of s is "9999999999"string *ps = new string(10, '9'); // *ps is "9999999999"string *ps = new string; // initialized to empty stringint *pi = new int; // pi points to an uninitialized intstring *ps = new string(); // initialized to empty stringint *pi = new int(); // pi points to an int value-initialized to 0delete释放动态分配的内存,并不是只要是指针就可以delete,只不过动态创建对象返回指针删除指针后,该指针变成悬垂指针。悬垂指针指向曾经存放对象的内存,但该对象已经不再存在了一旦删除了指针所指向的对象,立即将指针置为 0,这样就非常清楚地表明指针不再指向任何对象。C++ 允许动态创建 const 对象:const int *pci = new const int(1024);//尽管程序员不能改变 const 对象的值,
//但可撤销对象本身。如同其他动态对象一样
// const 动态对象也是使用删除指针来释放的:
delete pci; // ok: deletes a const object
隐式类型转换1.隐式类型转换:编译器进行,原则是尽可能防止精度损失int ival = 0;
ival = 3.541 + 3; // typically compiles with
// a warning.右边执行加法操作
//时是转换为double型操作,
//赋值给左值时,警告
2.有符号和无符号类型之间的转换:保持精度.例如:如果 int 型足够表示所有 unsigned short 型的值,则将 unsigned short 转换为 int.
补码就是对表达范围求模后的值,所以int型的值就是把其补码赋给了unsigned型的值.计算机中存储数据使用补码来存储的,对补码来说最高位不作为符号位,所以加法运算器可以以unsigned型直接进行位操作!!!同类型的赋值操作就是补码赋值,不同类型的时则保持精度的隐式转换
3.整型提升:所以比int小的类型,提升位int型
强制类型转换(推荐使用命名的强制类型转换cast-name<type>(expression))static_cast;dynamic_cast;const_cast(这个去除或添加const);reinterpret_cast
1.强制类型转换挂起了正常的类型检查(静态语言的标志)
2,尽量避免使用强制类型转换,比如副fun(char * a)这时,对一个string参数,可以通过string.c_str();获得const char*,但是不符合形参要求,可以使用const_cast转换,但是最好是用string.copy()获得一个char *型。