1、 枚举的语法不多做介绍,主要讲一下它在实际编程中怎么用:有些可以定义为整型的变量,取值被限定在一个有限的范围内,可取的值都有一个熟知的名字。例如,一个星期内的七天,一年的十二个月等。如果把这些量说明为字符串,则对这些变量进行关系运算或算术运算不方便(比如比较大小),而说明为整型每个值又没有名字也不太方便。这时后可考虑将其定义为枚举。使用枚举的好处是:不仅帮助程序员和用户记忆,而且使程序可靠,可以防止变量取非法取值。枚举在实际编程中非常常见,比如JAVA线程库中线程的状态(wait, start, run,)等等就是用枚举(而不是整型或字串),还有很多C程序对设备状态、寄存器状态等往往也用枚举标
2、识。Q1:在什么情况下使用枚举enum类型?好处是什么?2 寄存器变量register register int i;按语法的意思,是把i放到寄存器内,这比放到内存中快得多,如果i频繁使用的话,会提高效率。问题是C语言是可移值的某种程度上有跨平台的特性,不同平台编译register int i时会有不同的处理,为了防止副作用,有的C编译器可能不把i放到寄存器内。 大多数C语言编译器都含有优化器,对常使用的局部变量等等进行优化,有时没有声明为register的变量也会放到寄存器中,所以没必要使用register int i这种可移值性不好的代码。3 typedef 见“第四章 预处理指令”4 共
3、同体union 在C语言里,union可以看作是一种特殊的struct,而在C+里,struct和union可以看作是特殊的类,它们可以有构造函数。无论如何,union的基本特性是:使若干变量共享一段内存。比如: union myunion int x ;char ch; float y; a,b / 定义类型myunion。 x、ch、y共享其中类型最宽的字节的内存。最后定义了myunion类型变量a,b a.x; b.ch; b.y; /引用共同体成员 union在实际编程中有什么用?下面例举几种用途;(1)有人认为:不同类型变量共用一段内存,类型转换时不用强制转换。的确如此,但这绝不是好
4、的编程风格,如果只是为了类型转换方便,最好不要用union。(2)有时需要操作几个固定字节数、固定地址的硬件或软件接口(比如设备I/O端口),这个接口对不同代码的访问需要呈现不同的界面(比如有时是字符串,有时是整数,有时是控制字),可以使用union让几个成员共享该界面。 union portX lont int x ; / 4字节端口,I/O整数 char str4 ; /同一个4字节端口,I/O字串char c_word4; /同一个4字节端口,I/O控制字 myPort (3) 节约内存,在一些微小系统里,确实需要使用union节约内存,比如让多个数组共享一段内存。 在C+中,union
5、用作类的成员变量,由于类的构造函数的问题,可能会有一些需要特殊处理的问题,略过不讲。其它有关union语法的问题也略过不讲,我们只强调该怎么用它。在一些微小系统和底层程序中,union的出镜率还是挺高的。5 volatileC语言关键字volatile(易变的)表示不经过赋值,其值也可能被改变(例如,表示时钟的变量、表示端口的变量等)。声明为volatile的变量会阻止编译器对其优化(有时编译器会把变量优化到我们不希望的地方),在与硬件很密切的编程中使用较多。比如以下声明:volatile int a;a在运行期间不经过赋值也可能被改变。下面例举volatile变量的几种用途;1) 设备的硬件
6、寄存器(如:状态寄存器) 2) 一个中断服务子程序中会访问到的非自动变量3) 多线程应用中被几个任务共享的变量volatile还可以用来修饰常量:volatile const int a;这是什么意思?a不允许程序员修改,但会在程序运行过程中被系统修改,比如只读的状态寄存器。以前讲过,const真正的含义是“只读”,而不是“常数”。 对嵌入式编程或对硬件直接编程感兴趣的同学应注意volatile。6 浮点数的判等和比较 浮点数采用科学计数法,其值的精度不是100%精确,因此用于判等和比较时要特别小心。常用判等的方法是,两个浮点数相减的绝对值小于一个很小很小的数就认为相等。Q2:浮点数在判断是否
7、为0时会出现什么问题?原因是什么?二、C+类型 C+主要增加了类、泛型等类型,对struct也作了面向对象的升级。1 C+的class与structQ3:C+类与结构体区别?在C+里,struct可以看作是一个特殊的类,区别在于:class中默认的成员访问权限是private的,而struct中则是public的。从class继承默认是private继承,而从struct继承默认是public继承。 例:#include iostreamusing namespace std;struct ST ST() /结构体可以有构造函数和其它成员函数 int a; b; / 默认的成员访问权限是pub
8、lic ; /结构体成员函数实现的语法与类相同 int main(int argc, char *argv) ST st1( ); /定义结构体栈对象 ST *st2=new ST( ); /定义结构体堆对象 2C+的bool类型 C+的bool类型可取值为false和true,而旧式方式是0表示false,非0表示true.三、C/C+基本的类型转换 这一小节主要是复习性质的,大多数内容在C语言中学过。C/C+允许在一个表达式中参与运算的操作数的数据类型不一致,即支持不同数据类型的数据之间的混合运算。在对这样的表达式求值时,C+语言需要对其中的一些操作数进行类型转换。表达式中的类型转换有两种
9、方式:自动转换和强制转换。1自动转换 一般地,双目运算中的算术运算符、关系运算符、逻辑运算符和位操作运算符组成的表达式,要求两个操作数的类型一致,如果操作数类型不一致,则自动将低类型转换为高的类型。各种类型的高低顺序如下所示: 高 double float long unsigned 低 int char,short自动转换的特点: 自动转换由编译系统自动完成。 自动转换是一种保值映射,即在转换中数据的精度不受损失。 自动类型转换相当于“类型升级”,如果原来的数是无符号数,那么在扩展的时候,高位填充的是0;如果是有符号数,那么高位填充的是符号位。 可以自动转换的类型是“类型兼容”的,它们反过来
10、可以进型强制转换。自动转换一般只发生在算术表达式的运算中,因此,一般类型转换都应该用强制类型转换。2 强制转换 分为显式强制转换和隐式强制转换。(1)显式强制转换 有两种格式,例如: double f=3.55; int h; h=int (f); /第一种格式,函数风格 h=(int)f; /第二种格式,C风格 这两种格式的作用一是通知编译器,二是告诉程序的阅读者这里有类型转换,有些程序员把可以自动转换的类型也写成上述两种风格之一,这是好习惯。 double f; int h=5; f=double(h); 本来是可以自动转换的,但写成强制转换对程序的阅读者有更强的提示作用。 (2) 隐式强
11、制转换 主要有两种情况: 在赋值表达式中,右值类型可以隐式强制转换为左值类型。例如: double f=3.55; int h1; h=f; /隐式强制转换 另为在函数返回类型中,可以隐式强制转换,例如:int f1() return 36.8; ,实际返回36。但上述两种情况的有些类型转换必需进行显式强制转换,比如: int *p=100; /不能隐式强制转换,编译器会报错,应改为: int *p=(int *)100; 强制类型转换是不安全的,因为将高类型转为低类型会造成丢失小数部分精度损失或整数部分。这种情况只有程序员自已小心,在写强制类型转换代码时,最好不要写成隐式的,因为这会让程序阅
12、读者忽略转换可能带来的潜在危险,应该试一试可不可以写成显式的,对自动类型转换最好也试试可不可以写成显式的。 强制类型转换是“临时性”或“副本转换”的,比如: h=(int)f; /f“临时性”转换为3,转换完后f仍为3.55。Q4:为什么说强制类型转换是不安全的?3函数形参有类型转换功能函数实参传给形参,相当于赋值,所以只要形参和实参满足隐式强制转换条件(或自动转换条件),都能进行隐式强制转换(或自动转换)。Fun(int,int); /实参为float时可进行隐式强制转换Fun(float,float); /实参为int时可进行自动转换上述特性在C语言中就有,在C+中的成员函数和构造函数也具
13、有这些特性。但是,有时这种隐式类型转换可能带来一些问题,这时可用到C+关键字explicit,将类的构造函数声明为显示,也就是在声明构造函数的时候,前面添加上explicit即可,这样就可以防止这种转换操作。explicit只对C+构造函数有效。Q5:了解函数形参的类型转换功能四、C+对象的类型转换这一小节介绍C+特有的类型转换。C+的类型转换首先兼容C,也做了很多扩充,比C语言更精确,因此也更复杂。C+有了类和对象的概念,对象的形式是指针,因此C+对象的类型转换具有指针的一般特性,但是远比指针复杂,很少有一本编程书能介绍完整。1 RTTI Q6:什么是RTTI?学习C+类型转换,要知道一个概
14、念:RTTI (Run-Time Type Identification,运行时刻类型识别)。RTTI是面向对象一项重要技术,标准的C+以及很多编译器厂商的C+都实现了这项技术,另外JAVA/C#等也实现了这项技术,比如JAVA关键字instanceof需要RTTI支持。通过使用RTTI,程序可以在运行时通过基类指针或者引用来得到所指对象的实际类型。C+的RTTI主要有两个操作:(1)typeid操作符:返回指针或者引用所指对象的实际类型。(2)dynamic_cast操作符:将基类类型的指针或引用安全地转换为派生类型的指针或者引用。2 C+新增的type casting操作符 类型转换,英语
15、也叫type casting。C+新增了几个与type casting有关的操作符:static_castdynamic_castreinterpret_castconst_cast typeid(1)static_cast(不使用RTTI)用法:static_cast ( expression ) 该运算符把expression转换为TypeName类型,它是在编译时进行类型检查,而不使用RTTI。它主要有如下几种用法:用于类层次结构中基类和子类之间指针或引用的转换。进行上行转换(把子类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成子类表示)时,由于没有动态类型检
16、查,所以是不安全的。用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。相当于C语言的强制类型转换,是不安全的。double d; int a=static_cast(d);把某类型的空指针转换成目标类型的空指针。把任何类型的表达式转换成void类型。(2) dynamic_cast(使用RTTI)dynamic_cast a = 100;D *pd1 = static_cast(pb); /compile errorD *pd2 = dynamic_cast(pb); /pd2 is NULLdelete pb;在函数foo中,使用static_cast进行转换是不
17、被允许的,将在编译时出错,因为语法上不允许static_cast交叉转换;而使用 dynamic_cast的转换则是允许的,结果是空指针,这是dynamic_cast的特点,它更安全,它可以防止程序继续访问不该访问的成员(比如访问B的特有成员)。Q7:static_cast和dynamic_cast主要区别是什么?(3) reinpreter_castreinpreter_cast (expression)必须是一个指针、引用、算术类型、函数指针或者成员指针。reinterpret_cast可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。Reinterpret的含义就是“重新解释
18、”。reinterpret_cast “通常为操作数的位模式提供较低层的重新解释”,也就是说将数据以二进制存在形式的重新解释。 char*p=Thisisaexample.; int ireinterpret_cast(p);此时结果,i与p的二进制值是完全相同的,但解释不同:reinterpret_cast的作用是说将字符指针p的值以二进制(位模式)的方式被解释为整型,并赋给i,一个明显的现象是在转换前后没有数位损失。实际编程中,不推荐使用reinterpret_cast。(4) const_cast const_cast 该运算符用来去除expression中的const或volatile
19、属性。常见的转换是: 常量指针被转化成非常量指针,并且仍然指向原来的对象; 常量引用被转换成非常量引用,并且仍然指向原来的对象; 常量对象被转换成非常量对象。class B int a; const B b1; b1.a = 100; /comile error B b2 = const_cast(b1); /fine b2. a = 200;上面的代码编译时会报错,因为b1是一个常量对象,不能对它进行改变;但使用const_cast把它转换成一个非常量对象,就可以对它的数据成员任意改变。 C+另一个不常用的关键字mutalbe有时可以替代const_cast的作用,mutalbe的中文意思是
20、“可变的,易变的”,跟constant(const)是反义词。在C+中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态量。mutalbe常用在以下的成员函数内: class Aprivate:int a; void fun( ) const/ /含有修改类成员a 的代码以前讲过,void fun( ) const这种形式的函数,内部不允许修改类的成员变量,但有时又确实需要,这时可把int a声明为mutalbe int a。5typeid操作符(使用RTTI) typeid可以在运行时进行类型判断,表达式形如: typeid(express
21、ion);expression 是任意表达式或者类型名。如果表达式的类型是类类型且至少包含有一个虚函数(可以RTTI),则typeid操作符返回表达式的动态类型,需要在运行时计算;否则,typeid操作符返回表达式的静态类型,在编译时就可以计算。 typeid操作符的返回结果是名为type_info的标准库类型的对象的引用(在头文件typeinfo中定义)。C+标准并没有确切定义type_info,它的确切定义是与编译器相关的,但是标准却规定了其实现必需提供如下四种操作:t1 = t2如果两个对象t1和t2类型相同,则返回true;否则返回falset1 != t2如果两个对象t1和t2类型不
22、同,则返回true;t.name()返回类型的C-style字符串,类型名字用系统相关的方法产生t1.before(t2)返回指出t1是否出现在t2之前的bool值type_info类提供了public虚析构函数,以使用户能够用其作为基类。它的默认构造函数和拷贝构造函数及赋值操作符都定义为private,所以不能定义或复制type_info类型的对象。程序中创建type_info对象的唯一方法是使用typeid操作符(由此可见,如果把typeid看作函数的话,其应该是type_info类的友元)。type_info的name成员函数返回C-style的字符串,用来表示相应的类型名。下面看一个例
23、子(同学们可以自已上机看运行结果,不同的编译器运行结果可能不同):#include using namespace std;class Base ;class Derived: public Base ;int main()cout typeid(int).name() endl typeid(unsigned).name() typeid(long).name() typeid(unsigned long).name() typeid(char).name() typeid(unsigned char).name() typeid(float).name() typeid(double).name() typeid(string).name() type
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1