全国计算机等级考试辅导讲义(C++部分)(8)

2019-08-26 17:29

佳恒计算机教育培训中心

当在一个成员初始化列表中同时出现对虚基类和非虚基类构造函数的调用时,虚基类的构造函数先于非虚基类的构造函数执行。

【考点五】 子类型关系

公有继承时,派生类的对象可以作为基类的对象处理,派生类是基类的子类型。子类型关系使得在需要基类对象的任何地方都可以使用公有派生类的对象来替代,从而可以使用相同的函数统一处理基类对象和公有派生类对象(形参为基类对象时,实参可以是派生类对象),而不必为每一个类设计单独的处理程序,大大提高了程序的效率。它是实现多态性的重要基础之一。

子类型关系的定义如下:有一个特定的类型S,当且仅当它提供了类型T的行为时,称类型S是类型T的子类型。公有派生类的对象可以赋值给基类的对象。实际上不仅如此,具有子类型关系的基类和派生类的对象之间满足如下赋值兼容规则:

(1)公有派生类的对象可以赋值给基类的对象,即用公有派生类对象中从基类继承来的成员,逐个赋值给基类对象的成员。

(2)公有派生类的对象可以初始化基类的引用。

(3)公有派生类的对象的地址可以赋值给指向基类的指针。 【考点六】虚函数与多态性 1.多态性的概念

一个面向对象的系统常常要求一组具有相同基本语义的方法能在同一接口下为不同的对象服务,这就是所谓多态性(polymorphism)。在C++语言中,多态性可分为两类:编译时的多态性和运行时的多态性。编译时的多态性是通过函数重载和模板体现的。利用函数重载机制,在调用同名的函数时,编译系统可根据实参的具体情况确定所调用的是同名函数中的哪一个。

利用函数模板,编译系统可根据模板实参以及模板函数实参的具体情况确定所要调用的是哪个函数,并生成相应的函数实例;利用类模板,编译系统可根据模板实参的具体情况确定所要定义的是哪个类的对象,并生成相应的类实例。 由于有关操作所针对的具体目标(函数或类)的确定都是在编译时完成的,与运行时的动态环境无关,\编译时的多态性\因此而得名,其实现机制则和为静态绑定(static binding,也译作静态联编)。函数重载是\函数\一章中已经学习过的内容,但其中没有包含函数重载的一种特殊情况:运算符重载。 2.虚函数

在成员函数声明的前面加上virtual修饰,即把该函数声明为虚函数。虚函数可以是另一个类的友元函数,但不得是静态成员函数。在派生类中可以重新定义从基类继承下来的虚函数,从而提供该函数的适用于派生类的专门版本。也可能并不需要重新定义,在这种情况下,继承下来的虚函数仍然保持其在基类中的定义,即派生类和基类使用同一函数版本。

除少数特殊情况外,在派生类中重定义虚函数时,函数名、形参表和返回值类型必须保持不变。虚函数在派生类被重定义后,重定义的函数仍然是一个虚函数,可以在其派生类中再次被重定义。

注意:对于虚函数的重定义函数,无论是否用virtual修饰都是虚函数。当然,最好不要省略virtual修饰,以免削弱程序的可读性。

对虚函数的调用有两种方式:非多态调用和多态调用。 非多态调用是指不借助于指针或引用的直接调用。

非多态调用总是通过成员访问运算符 .进行的。与通常的成员函数调用类似,非多态调用是建立在静态绑定机制的基础之上的,不具备多态性特征。

主讲教师:刘老师 咨询辅导答疑:QQ(30724125)

佳恒计算机教育培训中心

多态调用是指借助于指向基类的指针或引用的调用。

在C++中,一个基类指针(或引用)可以用于指向它的派生类对象,而且通过这样的指针(或引用)调用虚函数时,被调用的是该指针(或引用)实际所指向的对象类的那个重定义版本。

基类中的实函数也可以在派生类中重定义,但重定义的函数仍然是实函数。在实函数的情况下,通过基类指针(或引用)所调用的只能是基类的那个函数版本,无法调用到派生类中的重定义函数。也就是说,尽管调用的语法形式可能是相同的,但对实函数的任何形式的调用都是非多态的。

注意:无论是虚函数还是实函数,在派生类中被重定义后,原来的函数版本即被隐藏,在通过成员访问运算符 .直接调用该函数时,所调用的是重定义版本。但原来的版本依然存在,仍然可以通过在函数名前加域修饰(即:<类名>::)来调用它们。 3.虚析构函数

析构函数也可以通过virtual修饰而声明为虚函数。

一个虚析构函数的版本被调用执行后,接着就要调用执行基类版本,依次类推,直到调用执行了派生序列的最开始的那个虚析构函数版本为止。通常,只要派生类中包含有虚函数的重定义(从而有可能被多态调用),而且对析构函数进行了专门的声明(而不是不做任何声明,从而采用默认的析构函数),其基类的析构函数就应当声明为虚函数,否则就可能出问题。 4.纯虚函数与抽象类

在某些情况下,基类无法确定(或无法完全确定)一个虚函数的具体操作方式或内容,只能靠派生类来提供各个具体的实现版本。基类中的这种必须靠派生类提供重定义版本的虚函数称为纯虚函数。为了将一个虚函数声明为纯虚函数,需要在虚函数原形的语句结束符 ;之前加上=0。

拥有纯虚函数的类称为抽象类,抽象类不能用来定义对象。

如果一抽象类的派生类没有重定义来自基类的某个纯虚函数,则该函数在派生类中仍然是纯虚函数,这就使得该派生类也成为抽象类。

也就是说,一个派生类可以把重定义纯虚函数的任务进一步转交给它自己的派生类。可以在将一个函数声明为纯虚函数的同时,为该函数提供实现版本。 换句话说,一个函数是否为纯虚函数,取决于其原形的尾部是否为\,与实现版本的有无没有什么关系。拥有实现版本的纯虚函数仍然有赖于派生类提供重定义版本。纯虚函数的实现版本通常是不完善的版本,但包含了一些共有操作,供各个派生类在重定义函数中调用。派生类在重定义一个纯虚函数时,可以继续将之声明为纯虚函数。另外,纯虚函数不得声明为内联函数。

第8章 运算符重载

【考点一】 运算符函数据与运算符重载

运算符重载是计算机语言固有多态性的体现,是构成计算机语言的基础之一。C++把重载的运算符视为特殊的函数,称为运算符函数。运算符重载就是函数重载的一种特殊情况。像对待一般重载函数一样,编译系统能够依据使用运算符的不同环境,即参数(操作数)的数量或类型的差异,区分同一运算符的不同含义。\运算符重载\是针对C++中原有运算符进行的,不可能通过重载创造出新的运算符。除了.、.*、::、?:、sizeof这五个运算符外,其他运算符都可以重载。

由于很多符号是一元运算符和二元运算符公用的,为了避免含混,不得为重载的运算符函数设置默认值,调用时也就不得省略实参。

除了new和delete这两个较为特殊运算符以外,任何运算符如果作为成员函数重载时不得重载为静态函数。 主讲教师:刘老师 咨询辅导答疑:QQ(30724125)

佳恒计算机教育培训中心

C++规定:=、[]、()、-> 、类型转换运算符只能作为类的成员函数重载,而且不能是针对枚举类型操作数的重载。 流插入运算符(<<)流提取运算符(>>)不能定义为类的成员函数。

运算符函数的函数名是由运算符前加关键字operator构成的,在声明运算符或调用运算符时都可以用这个名称。 【考点二】 典范运算符的重载 1.重载加法运算符\

“+”是一个二元运算符,因此作为成员函数重载时参数表中只有一个参数,对应于第二操作数,而第一操作数就是对象本身,仅以this指针的形式隐藏在参灵敏表中。\是一个典型的二元运算符,除赋值类运算符外的其他二元运算符的重载都可以参考这里描述的方法。 2.重载增1运算符\

++既可以是前缀运算符(前增1),又可以是后缀运算符(后增1)。为了区分这两种情况,重载这两个运算符时必须在格式上有所区别:重载后缀++时必须多一个虚拟参数:int,因此从形式上看像是一个二元运算符重载。 3.重载类型转换符

\类型转换符必须作为成员函数重载。在重载类型转换符时,由于运算符本身已经表示出返回值类型,因此不需要返回值类型的声明。 4.重载赋值运算符\

赋值运算符只能作为成员函数重载。常见的真正需要重载赋值运算符的情况是:类中包含指向动态空间的指针赋值运算符=的重载应注意以下几点:

①返回值声明为引用,而函数体中总是用语句return *this;返回; ②如果参数被声明为指向同类对象的引用或指针,应判别所指向对象的是否与被赋值对象为同一对象,如果是,立即返回,不做任何赋值处理;

③如果被赋值对象占用了动态空间或其他资源,应首先释放这些资源,以便接收新的资源; ④如果参数被声明为指针或引用,通常应加上const修饰; ⑤如果参数被声明为指针,应判别是否为空,以便做出特殊处理;

⑥一个类如果需要重载运算符=,通常也就需要定义自己特有的拷贝构造函数,反之亦然。 7.重载复合赋值运算符\

重载复合赋值类运算符,如+=、-=等,也应遵循上述重载赋值运算符的注意事项。与赋值运算符不同的是,复合赋值类运算符既可作为成员函数重载也可作为非成员函数重载。在后一种情况下,两个操作数都必须出现在参数表中;为了保持运算符原有的特性,第一参数应当声明为引用(否则就无法改变它的值),返回值也应当像重载\那样声明为引用,并在最后将获得新值的第一参数返回。 8.重载关系操作符\

重载的关系操作符函数应返回逻辑值。 9.重载下标访问运算符\运算符[]只能作为成员函数重载。 10.重载C++流运算符\和\

C++流的输入运算符>>和输出运算符<<只能作为非类成员函数重载。在一个类中,如有必要,可将<<或>>声明为友元函数。

【考点三】运算符重载应注意的几个问题 1?重载的运算符应保持其原有的基本语义

主讲教师:刘老师 咨询辅导答疑:QQ(30724125)

佳恒计算机教育培训中心

重载的运算符应该体现为原运算符的功能在新的数据类型上的延伸,它的使用应当使程序中算法的表达显得更流畅、自然,使阅读程序的人在不借助于其他说明资料的情况下就能够正确理解。不要让重载的运算符去勉强承担那些更适于一般函数承担的功能。

2.重载的运算符应尽可能保持基原有的特性

运算符的操作数个数、优先级和结合性是三个最基本的特性,而且是重载时自然得以保持的特性,因此无须采取专门的措施。需要注意的是下面这些特性。 ①是否要求第一操作数为有左值操作数。 ②是否修改第一操作数。

③操作的结果是否为有左值数据。 ④应保证第二操作数不被改变。

3.运算符的重载应当配套某些运算符之间关系密切,存在着某种逻辑上的联系,因此若需要重载其中的某一个,往往就意味着同组的其他运算符也需要重载。 4.使用引用参数还是非引用参数?

非引用参数的优点是:以传值方式传递参数,形参变量只是实参的副本,对形参变量的修改不会影响实参;在相关对象存在只需一个实参的构造函数的情况下,可以充分利用表达式处理过程中的自动转换机制,使表达式显得更自然。但当对象很大或需要深层复制时,非引用参数占用的计算机资源较多,影响参数传递的效率。引用参数的优点是:当对象很大或需要深层复制时,可大大减少对资源的占用,提高参数传递的效率。但无法利用系统的自动转换机制。 5.作为成员函数重载还是作为非成员函数重载?

=、[ ]、()、->以及所有的类型转换运算符只能作为成员函数重载。如果允许第一操作数不是同类对象,而是其他数据类型,则只能作为非成员函数重载(如输入输出流运算符<<和>>就是这样的情况)。若希望系统在必要时能够利用只需一个实参的构造函数自动对第一操作数进行转换,也应将该运算符作为非成员函数重载;此种情况下,运算符函数的参数应该是非引用参数,否则不能达到所希望的效果。其他情况下一般应作为成员函数重载。

主讲教师:刘老师 咨询辅导答疑:QQ(30724125)

佳恒计算机教育培训中心

第9章 模板

【考点一】 函数模板

1.函数模板的概念、定义与应用

函数模板是一系列相关函数的模型或样板,这些函数的源代码形式相同,只是所针对的数据类型不同。对于函数模板,数据类型本身成了它的参数,因而是一种参数化类型的函数。

声明一个函数模板的格式是:template?<模板形参表声明>?函数声明?

其中的<模板形参表声明>是由一个或多个<模板形参>组成(如果是多个,需要用逗号隔开)。 每个<模板形参>具有下面几种形式:

(1)typename?参数名?(2)class?参数名?(3)?类型修饰?参数名?

这里的?参数名?可以是任意的标识符。在这三种形式中,前两种是等价的:在声明模板参数时,关键字typename与class可以互换。用typename或class声明的参数称为虚拟类型参数;而用?类型修饰?声明的参数则挑为常规参数,在形式上与普通的函数参数声明相同。格式中的?函数声明?与一般函数的声明类似,只是某些类型修饰符被虚拟类型参数所替代。 2.模板实参的省略

在调用一个模板函数时,编译系统需要足够的信息来判别每个虚拟类型参数所对应的实际类型,可以从两个不同的渠道获得这样的信息:从模板实参表(用<和>括起来的参数表)或从模板函数实参表(用(和)括起来的参数表),模板实参的信息优先于函数实参的信息。如果从后者获得的信息已经能够判定其中部分或全部虚拟类型参数所对应的实际参数,而且它们又正好是参数表中最后的若干参数,则模板实参表中的那几个参数可以省略。

如果模板实参表中的实参都被省略了,则连空表也可以不要。因此,若x和y都是unsigned型,则add ?(x,y,9)、add(x、y、9)和add (x,y,9)三者是等价的。反之,对于某个模板实参,如果从模板函数的实参表中无法获得同样的信息,就不能省略;

或者虽然能够获得同样的信息,但在它后面还有其他不能省略的实参,则其自身还是不能省略。下面列举几种模板实参不能省略的情况。

(1)从模板函数实参表获得的信息矛盾。

(2)需要获得特定类型的返回值,而不管参数的类型如何。 (3)虚拟类型参数没有出现在模板函数的形参表中。 (4)函数模板含有常规形参。 3.函数模板的定制

对不同的数据类型在处理形式上的统一性是建立模板的基础。但是,这种统一性是相对的,个别数据类型有可能比较特殊,在处理形式上与大多数数据类型不一致。针对这样的特殊情况,可以通过重载模板函数进行定制。这样把重载的模板称为补充模板,相应地,原模板则称为主模板。 【考点二】类模板

1.类模板的概念、定义与应用

类模板就是一系列相关类的模型或样板,这些类的成员组成相同,成员函数的源代码形式相同,所不同的只是所针对的类型(成员的类型以及成员函数的参数和返回值的类型)。

对于类模板,数据类型本身成了它的参数,因而是一种参数化类型的类,是类的生成器。类模板中声明的类称为模板类。声明一个类模板的格式是:template??<模板形参表声明>?类声明

主讲教师:刘老师 咨询辅导答疑:QQ(30724125)

佳恒计算机教育培训中心

<模板形参表声明>是由一个或多个模板形参组成;与一般的类声明的不同之处在于,这里的<类声明>要用<模板形参表声明>中声明的虚拟类型参数来修饰它的某些成员,使模板类独立于任何具体的数据类型。

在模板外对成员函数的声明格式是:template<模板形参表声明>?返回类型?类名?<模板形参表>::函数名(函数形参表)?函数体?其中模板形参表就是由模板形参表声明中声明的参数名组成的序列。

应注意,类模板的成员函数都是模板函数,因此在模板外声明成员函数的格式与声明普通模板函数十分相似。 用类模板定义对象的格式是:类名<模板实参表>?对象名;或?类名<模板实参表>?对象名?(构造函数实参表); 在定义对象的过程中,编译系统会自动地根据需要生成相应的类定义,这种依据类模板生成类定义的过程称为类模板的实例化。

类模板实例所生成的每一个类定义就是相应类模板的一个实例。

在用类模板定义对象时,由于没有像函数实参表这样的额外信息渠道,因此无法按函数模板的方式省略模板实参。但是,可以为类模板的参数设置默认值。具体地说,在定义类模板时,可以为模板形参表声明的最后若干个参数设置默认值;而这些有默认值的参数中,最后的若干个对应实参可以在定义对象时省略。 2.类模板的派生与继承

一谈到继承总涉及到两个类:基类和派生类,它们中任何一个都有可能是模板类,或模板类的实例。下面列出常见的几种情况。

(1)普通类继承类模板的一个实例。可以通过继承类模板的一个实例来声明一个类。

(2)模板类继承普通类。在声明一个类模板时,应尽可能将类模板中与虚拟类型参数无关的成员剥离出来,构成一个普通类,作为类模板的基类。因此,类模板继承普通类的情况是十分常见的。 (3)模板类继承模板类。可以通过继承一个模板类来声明一个类模板。

(4)模板类继承模板参数给出的基类。模板的派生甚至可以继承一个未知的基类。也就是说,继承哪个基类由模板参数决定。

主讲教师:刘老师 咨询辅导答疑:QQ(30724125)


全国计算机等级考试辅导讲义(C++部分)(8).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:2010年国家公务员考试行测真题

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: