面向对象程序设计08-1.嵌套类与类型转换_第1页
面向对象程序设计08-1.嵌套类与类型转换_第2页
面向对象程序设计08-1.嵌套类与类型转换_第3页
面向对象程序设计08-1.嵌套类与类型转换_第4页
面向对象程序设计08-1.嵌套类与类型转换_第5页
已阅读5页,还剩40页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、面向对象程序设计嵌套类与类型转换在一个类的内部定义另一个类,称之为嵌套类(nested class),或嵌套类型(nested type)。之所以引入这样一个嵌套类,往往是因为外围类需要使用嵌套类对象作为底层实现,并且该嵌套类只用于外围类的实现。通过嵌套类机制,可以对用户隐藏该嵌套类的实现,减少全局的标识符,限制用户使用该类建立对象。嵌套类3嵌套类是其外围类的一个成员,像其他任何成员一样,外围类决定对这个类型的访问,在外围类的public部分定义的嵌套类是可在任何地方使用的类型,在protected部分的嵌套类是只能由外围类、友元或派生类访问的类型,在private部分的嵌套类是只能由外围类、

2、友元访问的类型。嵌套类的名字在其外围类的作用域中是可见的,但是在其他类域或名字空间中是不可见的,这意味着嵌套类的名字不会与另一作用域中声明的相同名字冲突。虽然嵌套类在外围类内部定义,但它是一个独立的类,基本上与外围类不相关。它的成员不属于外围类,外围类的成员也不属于该嵌套类。嵌套类的出现只是告诉外围类有一个这样的类型成员供外围类使用。外围类对嵌套类成员的访问没有任何特权,嵌套类对外围类成员的访问也同样如此,它们都遵循普通类的访问权限控制规则。嵌套类可以直接引用外围类的静态成员、类型名和枚举成员,即使这些是private的。5class Apublic: static int a; class

3、A1 void output() coutaendl; /instead of A:a; ; ;6嵌套类一般定义在外围类的类体内。嵌套类的成员函数可以其类体内定义,也可以在其类体外定义,但必须在外围类的同一作用域中,且要用外围类进行限定。嵌套类也可在外围类外或另一个头文件中定义,并在外围类中前向声明这个嵌套类即可。当然,在外围类外面定义这个嵌套类时,应该使用外围类进行限定。如果在另一个头文件中定义,使用时,只需要在外围类的实现文件中包含这个头文件即可。嵌套类成员的定义7/nestclass.h#ifndef NESTCLASS_H_#define NESTCLASS_H_ class Apub

4、lic: A(); A(); void operate();private: class B; B* m_b;#endif/nestclass.cpp#include nestclass.hclass A:Bpublic: B() B() void operate() coutB operate!endl;A:A() A:A() void A:operate() m_b = new B; coutA operate!operate();8比较好的设计是把嵌套类定义为外围类的私有成员,避免该嵌套类可以在整个程序中(在外围类的友元和成员定义之外)用作类型。有了这个方法,我们把嵌套类的所有成员都声明

5、为公有的也不再有任何坏处,因为只有外围类的友元和成员 可以访问嵌套类的成员。我们也不需要在嵌套类中对外围类进行友元声明了。9/示例嵌套类定义class List public:/ .private:class ListItem / 它的成员都是公有的public:ListItem( int val = 0 );ListItem *next;int value;ListItem *list;ListItem *at_end;10/ 用外围类名限定修饰嵌套类名List:ListItem:ListItem( int val ) value = val;next = 0;当我们没有在嵌套类体内以inl

6、ine 形式定义嵌套类的成员函数时,我们就必须在最外围的类之外定义这些成员函数,并用外围类名限定修饰嵌套类名。11在嵌套类的定义被看到之前,我们只能声明嵌套类的指针和引用。class List public:/ .private:/ 这个声明是必需的class ListItem;ListItem *list;ListItem at_end; /错误: 未定义嵌套类ListItem;注意12嵌套类不能直接访问其外围类的非静态成员,即使这些成员是公有的,任何对外围类的非静态成员的访问都要求通过外围类的指针,引用或对象来完成。例如:注意13class List public:int init( in

7、t );private:class ListItem public:ListItem( int val = 0 );void mf( const List & );int value;int memb;List:ListItem:ListItem( int val )/ List:init() 是类 List 的非静态成员value = init( val ); / 错误: 非法使用 init/ 必须通过 List 类型的对象或指针来使用14使用类的非静态成员时,编译器必须能够识别出非静态成员属于哪个对象。在ListItem的构造函数中的this指针的类型是ListItem* ,而要访问成员i

8、nit()所需的是List类型的对象或List*类型的指针。成员函数mf()通过引用参数引用init(),从这里我们能够知道成员init()是针对函数实参指定的对象而被调用的。void List:ListItem:mf( const List &il ) memb = il.init(); / ok: 通过引用调用init()15嵌套类可以直接访问外围类的静态成员,类型名,枚举值。类型名是一个typedef名字,枚举类型名,或是一个类名。例如:在ListItem的域之外,以及在外围类List域之外引用外围类的静态成员、类型名和枚举名都要求域解析操作符。注意16class List public

9、:typedef int (*pFunc)();enum ListStatus Good, Empty, Corrupted ;/ .private:class ListItem public:void check_status();ListStatus status; / okpFunc action; / ok/ .;/ .;17pFunc,ListStatus和ListItem都是外围类List 的作用域内部的嵌套类型名,这三个名字以及ListStatus的枚举值都可以被用在ListItem的域中,这些成员可以不加限定修饰地被引用。void List:ListItem:check_sta

10、tus()ListStatus s = status;switch ( s ) case Empty: .case Corrupted: .case Good: .18被用在嵌套类的定义中的名字(除了inline成员函数定义中的名字和缺省实参的名字之外)其解析过程如下:1.考虑出现在名字使用点之前的嵌套类的成员声明。2.如果第1步没有成功则考虑出现在名字使用点之前的外围类的成员声明。3.如果第2步没有成功则考虑出现在嵌套类定义之前的名字空间域中的声明。嵌套类域中的名字解析19enum ListStatus Good, Empty, Corrupted ;class List public:/

11、.private:class ListItem public:/ 查找:/ 1) 在 List:ListItem 中/ 2) 在 List 中/ 3) 在全局域中ListStatus status; / 引用全局枚举/ .;/ .;20编译器首先在类ListItem的域中查找ListStatus 的声明,因为没有找到成员声明,所以编译器接着在类List的域中查找ListStatus的声明,因为在List类中也没有找到声明,于是编译器在全局域中查找ListStatus的声明。在这三个域中只有位于ListStatus使用点之前的声明才会被编译器考虑,编译器找到了全局枚举ListStatus的声明,

12、它是被用在status声明中的类型。如果在全局域中,在外围域List之外定义嵌套类ListItem,则List类的所有成员都已经被声明完毕,因而编译器将考虑其所有声明,例如:21class List private:class ListItem;/ .public:enum ListStatus Good, Empty, Corrupted ;/ .;class List:ListItem public:/ 查找:/ 1) 在 List:ListItem 中/ 2) 在 List 中/ 3) 在全局域中ListStatus status; / List:ListStatus/ .;22List

13、Item的名字解析过程:首先在类ListItem的域中开始查找,因为没有找到成员声明,所以编译器在类List的域内查找ListStatus的声明,因为类List的完整定义都已经能够看得到,所以这一步查找考虑List的所有成员,于是找到List中嵌套的enum ListStatus,尽管它是在ListItem之后被声明的,status是List的ListStatus类型的一个枚举对象。如果List没有名为ListStatus的成员,则名字查找过程会在全局域中在嵌套类ListItem定义之前查找声明。23被用在嵌套类的成员函数定义中的名字,其解析过程如下:1.首先考虑在成员函数局部域中的声明。2.

14、如果第1步没有成功,则考虑所有嵌套类成员的声明。3.如果第2步没有成功,则考虑所有外围类成员的声明。4.如果第3步没有成功,则考虑在成员函数定义之前的名字空间域中出现的声明。嵌套类域中的名字解析24class List public:enum ListStatus Good, Empty, Corrupted ;/ .private:class ListItem public:void check_status();ListStatus status; / ok/ .;ListItem *list;/ .;int list = 0;void List:ListItem:check_status

15、()int value = list; / 哪个 list?25很有可能程序员想让check_status()中的List引用全局对象:value和全局对象list的类型都是int,List:list 成员是指针类型,在没有显式转换的情况它不能被赋给value不允许ListItem访问其外围类的私有数据成员listlist是一个非静态数据成员,在ListItem的成员函数中必须通过对象指针或引用来访问它尽管有这些原因,在成员check_status()中用到的名字list仍被解析为类List的数据成员list,外围类List的成员list隐藏了全局域中的对象,于是产生一个错误消息,因为在che

16、ck_status()中使用指针list是无效的。26只有在名字解析成功之后,编译器才会检查访问许可和类型兼容性。如果名字的用法本身就是错误的,则名字解析过程将不会再去查找更适合于该名字用法的声明,而是产生一个错误消息。 为访问全局对象list,需使用全局域操作符:void List:ListItem: check_status() value = :list; / ok如果成员函数check_status()被定义成位于ListItem类体中的内联函数,则上面修改会使编译器产生一个错误消息,报告全局域中的list没有被声明,因为编译器只考虑在check_status()定义之前可见的全局声明

17、。27类型转换就是将一种类型的值映射为另一种类型的值。主要包含隐式类型转换(implicit typeconversion)和显式类型转换(explicit type conversion)两种。隐式-自动显式-强制类型转换28C+定义了一组内置类型对象之间的标准转换,在必要时它们被编译器隐式地应用到对象上,隐式类型转换发生在下列这些典型的情况下:1.在混合类型的算术表达式中。在这种情况下,最宽的数据类型成为目标转换类型。这也被称为算术转换(arithmetic conversion)。例如:3.14159+32.用一种类型的表达式赋值给另一种类型的对象。在这种情况下,目标转换类型是被赋值对象

18、的类型。例如:int *pi = 0; int ival = 3.14159;隐式类型转换293.把一个表达式传递给一个函数调用,表达式的类型与形式参数的类型不相同。在这种情况下,目标转换类型是形式参数的类型。例如:int a=2; sqrt(2) 2-2.04.从一个函数返回一个表达式,表达式的类型与返回类型不相同。在这种情况下,目标转换类型是函数的返回类型。例如:double difference( int ival1, int ival2 )/ 返回值被提升为double 类型return ival1 - ival2;隐式类型转换30显式类型转换也被称为强制类型转换(cast),包括下列

19、强制类型转换操作符:static_cast,dynamic_cast,const_cast,reinterpret_cast,()等。显式类型转换31显式类型转换发生在下列这些典型的情况下:1.把void*型的指针赋值给任意显式类型时,C+要求显式强制转换。例如:int *pi = 0;void * pv = pi;char * pc = static_cast( pv );2.希望改变通常的标准转换。例如:double dval; int ival; ival += dval;ival += static_cast( dval );3.避免出现多种转换可能的歧义情况。显式类型转换32dyna

20、mic_cast一般被用来执行从基类指针到派生类指针的安全转换,常常被称为安全的向下转换。是在运行时刻执行的。失败返回0。manager *pm = dynamic_cast( pe );const_cast将转换掉表达式的常量性,例如:extern char *string_copy( char* );const char *pc_str;char *pc = string_copy( const_cast( pc_str );reinterpre_cast通常对于操作数的位模式执行一个比较低层次的重新解释,例如:complex * ;char *pc = reinterpret_cast(

21、 );显式类型转换33旧式强制类型转换char *pc = (char*) ;/C风格int ival = (int) 3.14159;/C风格int addr_value = int( &ival );/C+风格显式类型转换34C+提供了一种机制,通过它,每个类都可以定义一组可被应用在该类型对象上的转换。转换函数(conversion function)是一种特殊类型的类成员函数,它定义了一个由用户定义的转换,以便把一个类对象转换成某种其他的类型。在类体中通过指定关键字operator,并在其后加上转换的目标类型后我们就可以声明转换函数。转换函数又称类型强制转换成员函数,它是类中的一个非静态

22、成员函数。它的定义格式如下:转换函数35class public:operator ();这个转换函数定义了由到之间的映射关系。 可以是内置类型、类类型或typedef。下面通过一个例子说明转换函数的功能。转换函数36class Rationalpublic:Rational(int d, int n)den = d;num = n;operator double(); /类型转换函数private:int den, num;Rational:operator double()return double(den)/double(num);int main()Rational r(5, 8);d

23、ouble d = 4.7;d+=r;coutdendl;return 0;37程序输出结果:5.325由程序可知,d是一个double型数值,r是Rational类的对象,这两个不同类型的数据进行加法之所以能够进行是得益于转换函数 operator double()。为使上述加法能够进行,编译系统先检查类Rational的说明,看是否存在在下转换函数能够将Rational类型的操作数转换为 double类型的操作数。由于Rational类中说明了转换函数operator double(),它可以在程序运行时进行上述类型转换,因此,该程序中实现了d+=r;的操作。38定义转换函数时应注意如下几

24、点:1.转换函数是用户定义的成员函数,但它要是非静态的。2.转换函数的名称是类型转换的目标类型,因此,不必再为它指定返回值类型。3.转换函数是被用于本类型的数值或变量转换为其他的类型,也不必带参数。4.转换函数函数还不能定义为友元函数。5.不能定义到void的转换,也不允许转换成数组或者函数类型。6.转换常定义为const形式,原因是它并不改变数据成员的值。注意39转换操作符过于强大,它可以定义到一种内置类型的转换,然而这种内置类型本身是可能继续转换成其他的内置类型的,如operator int() const定义了到int型的转换,然后int型可以转换到float、double、long等类

25、型,这样极不容易确定编译器为我们做了什么,在出现问题后,也极不容易定位错误。当类类型定义了一个到基本类型A的转换后,可能在实际中转换到另一基本类型B,如果再定义一个到基本类型C的转换,而C也可以转换到B。这时就会在产生二义性,如A为int,B为long double,C为double,在产生自定义类型的对象时,需要转换,就会使得编译器并不知道如何处理,到底是调用哪个转换。可能带来的问题40使用自定义转换时,定义转换多,会像上述那样引发二义性。避免的方法是,最好在定义到内置类型的转换时,只定义一个,因为内置类型之间的转换太灵活了。另外,构造函数和转换函数之间也可能存在二义性。后面会讲到。可能带来的问题41在一个类的构造函数中,凡是只带一个参数的构造函数,都定义了一组隐式转换,把构造函数的参数类型转换为该类的类型。如果需要,编译器会在调用构造函数执行用户定义的转换之前,在实参上应用标准转换序列。下面通过一个例子进一步说明单参数构造函数的类型转换功能。构造函数作为转换函数42class A public:A() m=0; A

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论