类型转换在C++的很多代码中存在,更多的是在你看不到的背后执行的隐式类型转换。算术类型之间的转换是为了防止精度的丢失。如果左右操作数不一致且具备转换的条件,右操作数会被转换为左操作数类型。
类型转换发生在哪些地方:在混合表达式中表示范围较小的类型转换为表示范围较大的类型;当算术类型作为条件表达式中,算术类型为被转换为bool类型。
整形提升:整型里面比int小的(char,signedchar,unsigned char,short,unsigned)如果这些类型的范围都能包含于int类型内,那就转换为int类型,如果超出了int的表示范围,那就转换为诶unsigned int类型。如果将bool提升为int,那false转换为0,true转换为1。
有符号和无符号之间的转换:这类转换是依赖于机器中整型的相对大小的,因为表达式中设计到unsigned的转换都是依赖于机器的。当unsigned short包含于int时,就转换为int;否则两个都转换为unsigned int。如果unsigned int包含于long时,那unsigned int转换为long;否则两个都转换为unsigned long。
对于signed和unsigned int之间的转换通常会出乎我们的意料。表达式中的signed会被转换为unsigned int。如果signed恰巧是负数,那就会转换成一个很大的数值,带来副作用。至于为什么一个负数转换为unsigned后会变成很大的数值,这里面涉及到计算机关于有符号整型的存储机制以及运算机制。
大多数情况下数组名会转换为指向数组中第一个元素的同类型指针。
算术类型里面0会转换为bool里的false,其他值会转换为true。但从bool转换到整型的话,false转换为0,true则指定转换为1。
在C++中会自动将枚举类型转换为整型,至于转换为什么类型的整型,这个依赖于机器和枚举成员的最大值,原则是包含最大值的最小类型。如果枚举的成员值均在int范围内,就转换为int;否则就转换为unsigned int,long,unsignedlong等。
当使用非const对象初始化const对象引用时,会被转换为const对象;指针同理。
C++中即可以使用C的类型转换方式,同时也提供了4个专门用于显示类型转换的关键字。他们分别是static_cast,const_cast,dynamic_cast,reinterpret_cast。传统的转换都可以用static_cast来完成;const_cast的唯一用途是用于去除const和volatile属性;dynamic_cast用于运行时的类型识别,主要是用于类继承体系下的各个对象指针之间;reinterpret_cast的实现依赖于机器,主要用于对操作数的位模式在较低层次重新解释,这种转换是建立在使用者非常熟悉类型的表示以及编译器的情况下。
在switch-case语句中,每一个case(label)都是常量表达式。在开关语句中,只能在最后一个case语句或是default语句后面定义变量:防止代码跳过变量的定义与初始化。
Assert(断言机制)是一个预处理宏,只有在没有定义DEBUG宏时候才会检测括号里的表达式真假。一旦开发与测试工作完成,程序已经建立好,并定义了DEBUG宏。在产品代码中,assert遍不会工作。
当程序中产生异常并没有捕捉时会调用标准库函数terminate函数,终止程序。
在C++中使用调用操作符(其实就是一对圆括号,但也是一个操作符,并且还可以重载,函数对象就是基于此的)实现对函数的调用。
在早期的C++版本中,函数可以没有返回类型,其默认是int。但现在C++标准规定必须要返回类型,不过main函数是一个例外。在C++中main函数的标准是返回类型是int,当执行到最后发现没有返回值时,会自动返回0,表示正常结束。
每一次函数调用,都会开辟形参的局部空间,并用实参来初始化。
在函数的形参列表里,int和const int不算重载;但是在函数的后面const可作为重载的标识。
非const引用的形参只能关联完全同类型的引用:
Intincr(int &val)
{ Return ++val; }
Intmain()
{
Short v1 = 0;
Const int v2 = 1;
Int v3 = incr(v1); 错误,因为v1不是一个整型,不接受可转换类型
V3 = incr(v2); 错误,因为v2是一个const对象
V3 = incr(0); 错误,因为0不是左值
V3 = incr(v1 + v2); 错误,因为v1 + v2是表达式,不是左值
Int v4 = incr(v3); 正确,完全同类型
}
Void printValues(int *);
Void printValues(int []);
Void printValues(int [10]); 这三个函数声明式等价,因为编译器忽略形参的数组长度,只检测实参是否符合同类型指针,不会检测实参的数组长度信息。
通过引用传递数组:
和其他类型一样,数组也有引用,但数组元素内不允许存放引用,所以只有数组引用,没有引用数组。
如果形参是数组的引用,编译器就不会将其转换成指针,而是传递数组本身,这种情况下,编译器会检查实参的数组长度是否和形参规定的一致,不一致就会编译错误。所以传递数组引用可以保证函数内部的代码都是在数组的大小范围内。
Fun(int (&array)[10]);传递数组引用
Fun(int &array[10]); 形式上类似于引用数组,但没有这个名词,属语法错误
在传递多维数组时,编译器忽略第一维长度的检测:
Func(int (*array)[10]);
Fun(int array[][10]);这两句是等价的。
任何处理数组的程序都要确保程序是停留在数组的边界内。以下三种方式可以做到:1在数组内部标识结尾,类似于C风格字符串的空字符;2传递参数时,分别指定数组的第一个元素指针和数组最后一个元素下一个位置的指针,类似于标准库里面的左闭右开原则;3显示的传递数组的长度
含有可变形参的函数:C++中的省略符形参是为了编译使用了varargs的C语言程序。对于C++程序来说,只能将简单的基本类型数据传递给含有省略符形参的函数中。在无法准确列出函数所有实参的类型和数目时,可以使用省略符形参。省略符形参会暂停类型检查。
Void foo( parm_list, … );带有特定数目的形参
Void foo( … ); 不带形参
大部分带有省略符形参的函数都利用显示声明的参数中的一些信息,来获取其他可选实参的类型和数目。前一种情况较常用。
如果函数返回类型不是引用,在函数调用出将函数的返回值复制给临时对象。
如果函数返回值是引用,则不用产生复制。但一定要清楚返回的引用是关联哪个对象的,不能返回局部对象的引用或指针。