C++ 第13章 多态性(例子)
计算机二级C 专题 多态性和虚函数
{
w=k;h=l;
}
void fun(point &s) // 被动态联编
{
cout<<S.AREA()<< P>
}
void main()
{
Rectangle rec(3.0,5.2,15.0,25.0);
Fun(rec);
}
的实现中调用 act1(), 由于有两个 act1() 函数,并且是虚函数,产生
了动态联编,根据运行情况,选择了 B::act1();
. 如果将 A::act2() 的实现改为:
void A::act2()
{
this → act1();
}
输出结果如何?
输出结果: 375
. 派生类中对基类的虚函数进行替换时,要求派生类中说明的虚函数
与基类中被替换的虚函数之间满足如下条件:
. 与基类的虚函数有相同的参数个数。
. 其参数的类型与基类的虚函数的对应参数类型相同。
. 其返回值或者与基类虚函数相同,或者都返回指针或引用。
满足上述条件的派生类的成员函数,自然是虚函数,可以不加 virtual.
在运行时进行束定。
. 态联编只能通过指针或引用标识对象来操作虚函数。若采用一般类
型的标识对象来操作虚函数,则采用静态联编方式调用虚函数。
例如:一个动态联编的例子:
#include
class point
{
public:
point(double I,double j)
{x=I;y=j;}
virtual double Area()
{return 0.0;}
C语言中的多态
C语⾔中的多态⼀、多态的主要特点1、继承体系下。
继承:是⾯向对象最显著的⼀个特性。
继承是从已有的类中派⽣出新的类,新的类能吸收已有类的数据属性和⾏为,并能扩展新的能⼒,已有类被称为⽗类/基类,新增加的类被称作⼦类/派⽣类。
2、⼦类对⽗类的虚函数进⾏重写。
3、虚表。
在⾯向对象语⾔中,接⼝的多种不同现⽅式即为多态。
同⼀操作作⽤于不同的对象,可以有不同的解释,产⽣不同的执⾏结果,这就是多态性。
简单说就是允许基类的指针指向⼦类的对象。
⼆、代码实现1、C++中的继承与多态1 class Base2 {3 public:4 virtual void fun() {} //基类函数声明为虚函数5 int B1;6 };7 class Derived :public Base //Derived类公有继承Base类8 {9 public:10 virtual void fun() { //函数重写,此时基类函数可以声明为虚函数,也可以不声明11 cout << "D1.fun" << endl;12 }13 int D1;14 };15 int main(){16 Base b1; //创建⽗类对象17 Derived d1;//创建⼦类对象1819 Base *p1 = (Base *)&d1;//定义⼀个⽗类指针,并通过⽗类指针访问⼦类成员20 p1->fun();2122 Derived *p2 = dynamic_cast<Derived*> (&b1); //dynamic_cast⽤于将⼀个⽗类对象的指针转换为⼦类对象的指针或引⽤(动态转换)23 p2->fun();2425 getchar();26 return 0;27 }2. C语⾔实现C++的继承与多态1 typedef void(*FUNC)(); //定义⼀个函数指针来实现对成员函数的继承2 struct _Base //⽗类3 {4 FUNC _fun;//由于C语⾔中结构体不能包含函数,故借⽤函数指针在外⾯实现5 int _B1;6 };7 struct _Derived//⼦类8 {9 _Base _b1;//在⼦类中定义⼀个基类的对象即可实现对⽗类的继承10 int _D1;11 };12 void fb_() //⽗类的同名函数13 {14 printf("_b1:_fun()\n");15 }16 void fd_() //⼦类的同名函数17 {18 printf("_d1:_fun()\n");19 }20 int main() {21 _Base _b1;//定义⼀个⽗类对象_b122 _Derived _d1;定义⼀个⼦类对象_d12324 _b1._fun = fb_;//⽗类的对象调⽤⽗类的同名函数25 _d1._b1._fun = fd_;//⼦类的对象调⽤⼦类的同名函数2627 _Base *_p1 = &_b1;//定义⼀个⽗类指针指向⽗类的对象28 _p1-> _fun(); //调⽤⽗类的同名函数2930 _p1 = (_Base *)&_d1;//让⽗类指针指向⼦类的对象,由于类型不匹配所以要进⾏强转31 _p1->_fun(); //调⽤⼦类的同名函数3233 getchar();34 return 0;35 }。
多态的概念和作用(深入理解)
多态的概念和作⽤(深⼊理解)多态是⾯向对象的重要特性,简单点说:“⼀个接⼝,多种实现”,就是同⼀种事物表现出的多种形态。
编程其实就是⼀个将具体世界进⾏抽象化的过程,多态就是抽象化的⼀种体现,把⼀系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进⾏对话。
对不同类的对象发出相同的消息将会有不同的⾏为。
⽐如,你的⽼板让所有员⼯在九点钟开始⼯作, 他只要在九点钟的时候说:“开始⼯作”即可,⽽不需要对销售⼈员说:“开始销售⼯作”,对技术⼈员说:“开始技术⼯作”, 因为“员⼯”是⼀个抽象的事物, 只要是员⼯就可以开始⼯作,他知道这⼀点就⾏了。
⾄于每个员⼯,当然会各司其职,做各⾃的⼯作。
多态允许将⼦类的对象当作⽗类的对象使⽤,某⽗类型的引⽤指向其⼦类型的对象,调⽤的⽅法是该⼦类型的⽅法。
这⾥引⽤和调⽤⽅法的代码编译前就已经决定了,⽽引⽤所指向的对象可以在运⾏期间动态绑定。
再举个⽐较形象的例⼦:⽐如有⼀个函数是叫某个⼈来吃饭,函数要求传递的参数是⼈的对象,可是来了⼀个美国⼈,你看到的可能是⽤⼑和叉⼦在吃饭,⽽来了⼀个中国⼈你看到的可能是⽤筷⼦在吃饭,这就体现出了同样是⼀个⽅法,可以却产⽣了不同的形态,这就是多态!多态的作⽤:1. 应⽤程序不必为每⼀个派⽣类编写功能调⽤,只需要对抽象基类进⾏处理即可。
⼤⼤提⾼程序的可复⽤性。
//继承2. 派⽣类的功能可以被基类的⽅法或引⽤变量所调⽤,这叫向后兼容,可以提⾼可扩充性和可维护性。
//多态的真正作⽤,以前需要⽤switch实现----------------------------------------------------多态是⾯向对象程序设计和⾯向过程程序设计的主要区别之⼀,何谓多态?记得在CSDN⾥⼀篇论C++多态的⽂章⾥有⼀名话:“龙⽣九⼦,⼦⼦不同”多态就是同⼀个处理⼿段可以⽤来处理多种不同的情况,在钱能⽼师的《C++程序设计教程》书中有这样⼀个例⼦:定义了⼀个⼩学⽣类[本⽂全部代码均⽤伪码]class Student{public:Student(){}~Student(){}void 交学费(){}//......};⾥⾯有⼀个 “交学费”的处理函数,因为⼤学⽣和⼩学⽣⼀些情况类似,我们从⼩学⽣类中派⽣出⼤学⽣类:class AcadStudent:public Student{public:AcadStudent(){}~ AcadStudent(){}void 交学费(){}//.......};我们知道,中学⽣交费和⼤学⽣交费情况是不同的,所以虽然我们在⼤学⽣中继承了中学⽣的"交学费"操作,但我们不⽤,把它重载,定义⼤学⽣⾃⼰的交学费操作,这样当我们定义了⼀个⼩学⽣,⼀个⼤学⽣后:Student A;AcadStudent B;A.交学费(); 即调⽤⼩学⽣的,B.交学费();是调⽤⼤学⽣的,功能是实现了,但是你要意识到,可能情况不仅这两种,可能N种如:⼩学⽣、初中⽣、⾼中⽣、研究⽣.....它们都可以以Student[⼩学⽣类]为基类。
【C++面向对象的程序设计】6多态性
虚析构函数
析构函数的作用是对象撤销之后清理现场。 在派生类对象撤销时,一般先调用派生类的 析构函数。再调用基类的析构函数。
然而,当定义的是一个指向基类的指针变量, 使用new运算符建立临时对象时,如果基类 中有析构函数,则在使用delete析构时只会 调用基类的析构函数。
这就需要将基类中的析构函数声明为虚函数。
虚函数的声明与使用
声明虚函数的一般格式如下: virtual 函数原型;
⑴ 必须首先在基类中声明虚函数。 ⑵ 派生类中与基类虚函数原型完全相同的成员函 数,即使在说明时前面没有冠以关键字virtual也 自动成为虚函数。
声明虚函数
⑶ 只有非静态成员函数可以声明为虚函数。 ⑷ 不允许在派生类中定义与基类虚函数名字及参数 特征都相同,仅仅返回类型不同的成员函数。 编译时 出错。 ⑸ 系统把函数名相同但参数特征不同的函数视为不 同的函数。 ⑹ 通过声明虚函数来使用C++提供的多态性机制时, 派生类应该从它的基类公有派生。
构函数等内容。
本章内容
静态联编与动态联编 虚函数的声明与使用 纯虚函数和抽象类 虚析构函数
Hale Waihona Puke 静态联编与动态联编所谓联编(tinding),就是使一个计算机程序的不同部 分彼此关联的过程。
静态联编在编译阶段完成,因为所有联编过程都在程 序开始运行之前完成,因此静态联编也叫先前联编或早期 联编。
另一种情况编译程序在编译时并不确切知道应把发送 到对象的消息和实现消息的哪段具体代码联编在一起,而 是在运行时才能把函数调用与函数体联系在一起,则称为 动态联编。
动态联编的实现
C ++语言中的动态联编是通过使用虚函数表 (Virtual Function Table)来实现的,虚函数表也称 为v-表。
C一教学
面向对象的基本概念
对象 属性 服务 对象标识
对象
对象标识
属性
服务
公司职员
姓名 身份证号
......
股东 股份
职员 工资
面向对象的基本概念
类 一般类 特殊类 抽象
分类——人类通常的思维方法 分类所依据的原则——抽象
– 忽略事物的非本质特征,只注意那些与当前目标有 关的本质特征,从而找出事物的共性,把具有共同 性质的事物划分为一类,得出一个抽象的概念。
– 例如,石头、树木、汽车、房屋等都是人们在长期 的生产和生活实践中抽象出的概念。
面向对象的基本概念
类 一般类 特殊类 抽象
面向对象方法中的"类"
– 具有相同属性和服务的一组对象的集合 – 为属于该类的全部对象提供了抽象的描述,包括属性和行为
两个主要部分。 – 类与对象的关系:
犹如模具与铸件之间的关系,一个属于某类的对象称为该类 的一个实例。
继承(继承,单继承,多继承) 消息
– 是向对象发出的服务请求
聚合
– 一个(较复杂的)对象由其他若干(较简单的)对象作为其 构成部分
面向对象的基本概念
封装 继承 消息 聚合 关联
两种方式: 整体对象
部分对象
嵌套对象
整
部分对象
体
对
象 部分对象
整体对象
面向对象的基本概念
封装 继承 消息 聚合 关联
习题板 习题组 所属课程 布置时间 完成期限 m 选题 查阅题目 公布答案 查阅答案
考试题板 m 答卷
分数公布 收卷 阅卷
班 班级名称 m 学生名单 m
.....
练习本 1 使用者 课程名 0,1 习题解答
面向对象的三大特征之一:多态性
⾯向对象的三⼤特征之⼀:多态性⼀多态:多态指的是⼀类事物有多种形态。
⼀般是抽象的形式,它们之间是继承的关系。
import abcclass Animal(metaclass=abc.ABCMeta):@abc.abstractmethoddef run(self):pass@abc.abstractmethoddef eat(self):passclass People(Animal):def run(self):print('People is walking')def eat(self):print('People is eating ')class People(Animal):def run(self):print('People is walking')# def eat(self):# print('People is eating ')class Pig(Animal):def run(self):print('Pig is walking')def eat(self):print('Pig is eating ')分析总结:从上⾯的代码中可以看出,Animal有三种形态,分别是,⼈,狗,猪。
⽽且也体现了都是继承的关系‘猪是动物’。
多态性(1)什么是多态性(注意:多态与多态性是两种概念)多态性是指具有不同功能的函数可以使⽤相同的函数名,这样就可以⽤⼀个函数名调⽤不同内容的函数。
在⾯向对象⽅法中⼀般是这样表述多态性:向不同的对象发送同⼀条消息,不同的对象在接收时会产⽣不同的⾏为(即⽅法)。
总⽽⾔之就是在不考虑对象的类型的情况下直接使⽤对象。
(1)增加了程序的灵活性 以不变应万变,不论对象千变万化,使⽤者都是同⼀种形式去调⽤,如func(animal)(2)增加了程序额可扩展性 通过继承animal类创建了⼀个新的类,使⽤者⽆需更改⾃⼰的代码,还是⽤func(animal)去调⽤class Cat(Animal):def run(self):print('say miaomiao')def fun(obj):obj()cat1=Cat()Cat类是在上⾯的类型中新添加的,但同样可以直接使⽤fun():鸭⼦理论python是⼀种多态语⾔,崇尚鸭⼦类型。
C语言中的多态与继承
C语言中的多态与继承多态和继承是面向对象编程中两个重要的概念。
它们不仅在C++等高级语言中有着广泛的应用,而且在C语言中也具备一定的实现方式。
本文将讨论C语言中的多态与继承,探讨它们的概念、特点以及在实际编程中的应用。
一、多态的概念与特点多态是指同一操作作用于不同的对象,可以有不同的解释和实现方式。
在C语言中,要实现多态性通常使用函数指针和结构体来模拟。
通过函数指针,可以实现对不同结构体中相同类型的成员进行访问,进而实现多态。
多态的特点有以下几个方面:1. 同一操作作用于不同对象,可以有不同的表现形式。
2. 多态性可以增加代码的灵活性和可扩展性。
3. 多态性可以提高代码的复用性和可读性。
二、继承的概念与特点继承是面向对象编程中的基本概念之一,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。
在C语言中,要实现继承通常使用结构体嵌套的方式来模拟。
继承的特点有以下几个方面:1. 子类可以拥有父类的属性和方法。
2. 子类可以覆盖父类的方法,实现自己的特定功能。
3. 继承可以实现代码的重用和扩展,提高代码的效率和可维护性。
三、C语言中多态与继承的应用在C语言中,多态和继承可以通过结构体、函数指针以及函数调用的方式来实现。
首先,我们需要定义一个基类结构体,包含一些通用的属性和方法。
然后,针对不同的具体情况,可以定义多个不同的派生类结构体,继承基类的属性和方法,并在派生类中实现自己特定的操作。
接下来,我们需要定义一个函数指针成员,用于指向不同派生类中的方法。
通过函数指针的动态绑定,可以在运行时确定调用哪一个具体的方法,实现多态的效果。
最后,在调用函数的时候,可以使用基类的指针指向不同的派生类对象,通过函数指针调用对应的方法。
由于函数指针的动态绑定,程序会根据对象的实际类型来决定调用哪个方法,实现多态的效果。
通过上述方式,我们可以在C语言中模拟出多态和继承的特性,实现代码的复用、扩展和灵活调用。
药物代谢酶基因多态性与药物治疗个体化
药物代谢酶基因多态性与药物治疗个体化第一章:引言药物代谢酶基因多态性是指个体之间存在的基因组变异,这些变异影响了药物在体内的代谢速度和效果,从而导致不同个体对同一药物的反应差异。
药物代谢酶基因多态性是实现个体化药物治疗的重要基础。
第二章:药物代谢酶基因多态性的分类目前,药物代谢酶基因多态性主要分为CYP酶、UGT酶和GST酶三种。
其中,CYP酶是药物代谢酶中最为重要的一类,它参与了70%以上药物代谢的过程。
CYP酶基因主要包括CYP1A2、CYP2C9、CYP2C19、CYP2D6和CYP3A4等。
第三章:药物代谢酶基因多态性与药物治疗的个体化药物代谢酶基因多态性是药物治疗个体化的重要基础。
根据不同的基因型,个体之间可能出现药物代谢速度的差异,从而导致药物在体内的浓度不同,影响药物的疗效和安全性。
因此,在临床使用药物时,应根据患者的基因型,制定个体化的用药方案,以适应不同个体的药物代谢特征,从而有效提高药物治疗的效果和安全性。
第四章:临床应用药物代谢酶基因多态性的例子在临床上,药物代谢酶基因多态性已经被广泛应用。
以华法林为例,它是目前治疗静脉血栓栓塞症最常用的口服抗凝药物之一。
华法林的代谢过程受到CYP2C9和VKORC1基因的影响,而CYP2C9基因的多态性是导致华法林摄入后出现出血等不良反应的主要原因之一。
因此,根据患者的基因型,调整华法林的剂量就可以减少不良反应的发生,提高治疗效果。
另一例是阿司匹林。
一项针对大约8000名心脏病患者的研究发现,CYP2C19*2、CYP2C19*3和CYP2C19*17等位基因的多态性可以影响阿司匹林的代谢速度,进而影响阿司匹林的抗血小板作用和不良反应发生率。
根据基因型,制定个体化的用药方案就可以缩小阿司匹林出现不良反应的风险,提高治疗效果。
第五章:未来展望药物代谢酶基因多态性已经成为了探索个体化药物治疗的重要领域。
未来,随着高通量测序技术和基因分型技术的发展,个体化药物治疗将有更多的突破和应用。
面向对象编程中的多态性
面向对象编程中的多态性近年来,随着计算机技术的飞速发展,在软件工程中,面向对象编程(Object-Oriented Programming,OOP)逐渐成为了一种主流的编程思想,也是一种被广泛使用的编程范式。
面向对象编程在软件工程中的应用已经越来越广泛,目前已经成为了大多数编程语言中最基本的组成部分。
在面向对象编程中,多态性(polymorphism)是一种非常重要的概念,是面向对象编程语言的主要特性之一。
一、多态性的概念多态性是面向对象编程的核心概念之一,代表一种对象多态化的能力。
面向对象编程中的多态性是指对于同一类型的不同对象的同一操作,在运行时可以有不同的行为,即同一方法的不同实现方式。
换句话说,多态性是一种变化的表现形式,可以在不改变程序的前提下,动态地改变对象的类型,实现灵活和高效的程序设计。
多态性需要满足三个条件:继承、重写和向上转型。
继承是面向对象编程语言所具备的一种特性,即子类可以继承父类的属性和方法。
重写是指在一个子类中重写父类的某个方法。
向上转型是指将一个子类对象作为父类对象进行处理,从而实现对多态性的应用。
这三个条件的结合,使得类的设计更加灵活,可以有效地实现代码重用。
二、多态性的实现方式在面向对象编程中,多态性可以通过以下几种方式来实现:1、方法重载方法重载是指在一个类中定义了多个同名的方法,但是它们有不同的参数列表。
在调用这些方法时,根据不同的参数列表来匹配具体的方法。
方法重载是一种静态的多态性,即在编译时就能确定具体的方法。
2、方法重写方法重写是指子类可以重新定义父类中的某个方法。
在调用这个方法时,会根据实际对象的类型来调用相应的方法。
方法重写是一种动态的多态性,即在运行时才能确定具体的方法。
3、抽象类与接口抽象类和接口都可以用来实现多态性。
抽象类是一种特殊的类,不能被实例化,只能被继承。
在抽象类中定义抽象方法,具体的实现交给子类去完成。
接口是一种纯抽象的类,其中只定义了方法的签名,而没有具体的实现。
c 多态性实验报告
c 多态性实验报告C++多态性实验报告引言:多态性是面向对象编程中的一个重要概念,它允许我们以一种统一的方式处理不同类型的对象。
在C++中,多态性通过虚函数和指针或引用来实现。
本实验旨在探索C++中多态性的实际应用,并通过实验结果来验证其效果。
实验步骤:1. 创建基类和派生类:首先,我们创建一个基类Animal,其中包含一个虚函数makeSound()用于发出动物的声音。
然后,我们创建两个派生类Dog和Cat,它们分别继承自Animal 类,并实现自己的makeSound()函数。
2. 创建动态数组:接下来,我们创建一个动态数组,其中包含不同类型的动物对象。
这样我们就可以在一个数组中存储不同类型的对象,并以统一的方式处理它们。
3. 调用虚函数:通过使用基类指针或引用,我们可以调用派生类中的虚函数。
这样,无论基类指针指向的是派生类的对象还是基类的对象,都可以正确地调用派生类的函数。
我们可以通过遍历动态数组来调用每个动物对象的makeSound()函数,并观察到不同类型的动物发出不同的声音。
实验结果:我们创建了一个动态数组,其中包含了两个Dog对象和两个Cat对象。
通过遍历数组并调用makeSound()函数,我们观察到每个动物对象都发出了与其类型相对应的声音。
这证明了多态性的实际应用,即通过统一的接口处理不同类型的对象。
讨论与总结:多态性是面向对象编程中的重要概念,它提供了一种灵活的方式来处理不同类型的对象。
通过使用虚函数和基类指针或引用,我们可以以统一的方式处理派生类对象,并实现动态绑定。
这种灵活性使得我们的代码更加可扩展和可维护。
然而,多态性也带来了一些性能开销。
由于需要在运行时确定函数的调用地址,多态性可能会导致一定的性能损失。
因此,在实际编程中,我们需要根据具体情况来权衡使用多态性的利与弊。
总之,本实验通过实际应用验证了C++中多态性的效果,并讨论了其优缺点。
多态性是面向对象编程中的重要概念,对于提高代码的可扩展性和可维护性具有重要意义。
多态概念内容
多态概念内容多态(polymorphism)是计算机科学中的一个基本概念,指的是在面向对象编程中,对象能够以不同的方式被调用,而不需要重新创建一个新的对象。
具体来说,一个对象可以通过其属性和方法的组合,表现出不同的行为,而这些行为可以是抽象的(即通用的行为,不考虑具体实现细节)或具体的(即针对特定的对象或场景进行的行为)。
多态的实现方式有很多种,其中最常见的是继承( inheritance )和接口(interface)。
继承是一种将一个类的一部分行为继承到另一个类中的方法,而接口则是一种定义一组方法的协议,让不同的类可以调用这些方法,而不需要知道具体的实现细节。
除了继承和接口,还有其他一些实现多态的方式,如封装(封装是一种将对象的行为和数据分开的方法)和函数式编程(Functional Programming)中的lambda 表达式。
函数式编程中的 lambda 表达式可以用于封装对象的行为,从而隐藏对象的实现细节,使得在调用对象时只需要调用表达式返回的结果即可。
多态在面向对象编程中具有非常重要的意义,能够使得程序更加模块化、易于维护和扩展。
在实际应用中,多态也经常被用于实现客户端和服务端的分离,使得客户端只需要关注其需要的功能,而不需要关注整个系统的实现细节。
拓展:除了计算机科学中常见的多态概念,数学中的多态也可以指代一种函数的多重定义。
在数学中,多重定义指的是同一个函数在不同的上下文中定义了多个不同的函数。
例如,在函数的定义中,可以定义多个函数的参数类型和返回值类型,使得函数可以在不同的上下文中具有不同的功能和用途。
这种多重定义在计算机科学中也可以被称为多态,因为同一个函数可以在不同的上下文中表现出不同的行为。
现代编译原理C语言描述
作者简介Andrew W.Appel,美国普林斯顿大学计算机科学系教授,第26届ACM SIGPLAN-SIGACT程序设计原理年会大会执行主席,1998-1999年在贝尔实验室做研究工作。
主要研究方向是计算机安全、编译器设计、程序设计语言等。
本书简介 本书全面讲述了现代编译器的各个组成部分,包括词法分析、语法分析、抽象语法、语义检查、中间代码表示、指令选择、数据流分析、寄存器分配以及运行时系统等。
全书分成两部分,第一部分是编译的基础知识,适用于第一门编译原理课程(一个学期);第二部分是高级主题,包括面向对象语言和函数语言、垃圾收集、循环优化、SSA(静态单赋值)形式、循环调度、存储结构优化等,适合于后续课程或研究生教学。
书中专门为学生提供了一个用C语言编写的实习项目,包括前端和后端设计,学生可以在一学期内创建一个功能完整的编译器。
本书适用于高等院校计算机及相关专业的本科生或研究生,也可供科研人员或工程技术人员参考。
目录第一部分 编译基本原理第1章 绪论 11.1 模块与接口 11.2 工具和软件 31.3 树语言的数据结构 3程序设计:直线式程序解释器 7推荐阅读 9习题 9第2章 词法分析 102.1 词法单词 102.2 正则表达式 112.3 有限自动机 132.4 非确定有限自动机 152.4.1 将正则表达式转换为NFA 162.4.2 将NFA转换为DFA 182.5 Lex:词法分析器的生成器 20程序设计:词法分析 22推荐阅读 23习题 23第3章 语法分析 273.1 上下文无关文法 283.1.1 推导 293.1.2 语法分析树 293.1.3 二义性文法 303.1.4 文件结束符 313.2 预测分析 323.2.1 FIRST集合和FOLLOW集合 333.2.2 构造一个预测分析器 353.2.3 消除左递归 363.2.4 提取左因子 373.2.5 错误恢复 373.3 LR分析 393.3.1 LR分析引擎 403.3.2 LR(0)分析器生成器 413.3.3 SLR分析器的生成 443.3.4 LR(1)项和LR(1)分析表 453.3.5 LALR(1)分析表 463.3.6 各类文法的层次 473.3.7 二义性文法的LR分析 473.4 使用分析器的生成器 483.4.1 冲突 493.4.2 优先级指导 503.4.3 语法和语义 533.5 错误恢复 543.5.1 用error符号恢复 543.5.2 全局错误修复 55程序设计:语法分析 57推荐阅读 58习题 58第4章 抽象语法 624.1 语义动作 624.1.1 递归下降 624.1.2 Yacc生成的分析器 624.1.3 语义动作的解释器 644.2 抽象语法分析树 654.2.1 位置 674.2.2 Tiger的抽象语法 68程序设计:抽象语法 71推荐阅读 71习题 72第5章 语义分析 735.1 符号表 735.1.1 多个符号表 745.1.2 高效的命令式风格符号表 75 5.1.3 高效的函数式符号表 765.1.4 Tiger编译器的符号 775.1.5 函数式风格的符号表 795.2 Tiger编译器的绑定 795.3 表达式的类型检查 825.4 声明的类型检查 845.4.1 变量声明 845.4.2 类型声明 855.4.3 函数声明 855.4.4 递归声明 86程序设计:类型检查 87习题 87第6章 活动记录 896.1 栈帧 906.1.1 帧指针 916.1.2 寄存器 926.1.3 参数传递 926.1.4 返回地址 946.1.5 栈帧内的变量 946.1.6 静态链 956.2 Tiger编译器的栈帧 966.2.1 栈帧描述的表示 986.2.2 局部变量 986.2.3 计算逃逸变量 996.2.4 临时变量和标号 100 6.2.5 两层抽象 1006.2.6 管理静态链 1026.2.7 追踪层次信息 102程序设计:栈帧 103推荐阅读 103习题 103第7章 翻译成中间代码 106 7.1 中间表示树 1067.2 翻译为树中间语言 108 7.2.1 表达式的种类 1087.2.2 简单变量 1117.2.3 追随静态链 1127.2.4 数组变量 1137.2.5 结构化的左值 1147.2.6 下标和域选择 1147.2.7 关于安全性的劝告 115 7.2.8 算术操作 1167.2.9 条件表达式 1167.2.10 字符串 1177.2.11 记录和数组的创建 118 7.2.12 while循环 1197.2.13 for循环 1197.2.14 函数调用 1207.3 声明 1207.3.1 变量定义 1207.3.2 函数定义 1207.3.3 片段 121程序设计:翻译成树 122习题 123第8章 基本块和轨迹 1258.1 规范树 1268.1.1 ESEQ的转换 1268.1.2 一般重写规则 1268.1.3 将CALL移到顶层 130 8.1.4 线性语句表 1318.2 处理条件分支 1318.2.1 基本块 1318.2.2 轨迹 1328.2.3 完善 1338.2.4 最优轨迹 133推荐阅读 134习题 134第9章 指令选择 1369.1 指令选择算法 1389.1.1 Maximal Munch算法 1389.1.2 动态规划 1409.1.3 树文法 1419.1.4 快速匹配 1439.1.5 覆盖算法的效率 1439.2 CISC机器 1449.3 Tiger编译器的指令选择 1469.3.1 抽象的汇编语言指令 1469.3.2 生成汇编指令 1489.3.3 过程调用 1519.3.4 无帧指针的情形 151程序设计:指令选择 152推荐阅读 153习题 154第10章 活跃分析 15510.1 数据流方程的解 15610.1.1 活跃性计算 15610.1.2 集合的表示 15810.1.3 时间复杂度 15810.1.4 最小不动点 15910.1.5 静态活跃性与动态活跃性 16010.1.6 冲突图 16110.2 Tiger编译器的活跃分析 16210.2.1 图 16210.2.2 控制流图 16310.2.3 活跃分析 164程序设计:构造流图 164程序设计:活跃分析模块 165习题 165第11章 寄存器分配 16611.1 通过简化进行着色 16611.2 合并 16811.3 预着色的结点 17111.3.1 机器寄存器的临时副本 17111.3.2 调用者保护的寄存器和被调用者保护的寄存器 172 11.3.3 含预着色结点的例子 17211.4 图着色的实现 17511.4.1 传送指令工作表的管理 17611.4.2 数据结构 17611.4.3 程序代码 17711.5 针对树的寄存器分配 181程序设计:图着色 184推荐阅读 185习题 185第12章 整合为一体 188程序设计:过程入口/出口 189程序设计:创建一个可运行的编译器 191第二部分高级主题第13章 垃圾收集 19313.1 标记-清扫式收集 19413.2 引用计数 19713.3 复制式收集 19813.4 分代收集 20113.5 增量式收集 20313.6 Baker算法 20513.7 编译器接口 20513.7.1 快速分配 20513.7.2 数据布局的描述 20613.7.3 导出指针 207程序设计:描述字 208程序设计:垃圾收集 208推荐阅读 208习题 210第14章 面向对象的语言 21114.1 类 21114.2 数据域的单继承性 21314.3 多继承 21414.4 测试类成员关系 21614.5 私有域和私有方法 21814.6 无类语言 21914.7 面向对象程序的优化 219程序设计:OBJECT-Tiger 220推荐阅读 220习题 221第15章 函数式程序设计语言 22215.1 一个简单的函数式语言 22215.2 闭包 22415.3 不变的变量 22515.3.1 基于延续的I/O 22615.3.2 语言上的变化 22715.3.3 纯函数式语言的优化 22815.4 内联扩展 22915.5 闭包变换 23315.6 高效的尾递归 23515.7 懒惰计算 23615.7.1 传名调用计算 23715.7.2 按需调用 23815.7.3 懒惰程序的计算 23915.7.4 懒惰函数式程序的优化 23915.7.5 严格性分析 241推荐阅读 243程序设计:编译函数式语言 244习题 244第16章 多态类型 24616.1 参数多态性 24616.1.1 显式带类型的多态语言 24716.1.2 多态类型的检查 24816.2 类型推论 25316.2.1 一个隐式类型的多态语言 254 16.2.2 类型推论算法 25516.2.3 递归的数据类型 25716.2.4 Hindley-Milner类型的能力 259 16.3 多态变量的表示 25916.3.1 多态函数的扩展 26016.3.2 完全的装箱转换 26116.3.3 基于强制的表示分析 26216.3.4 将类型作为运行时参数传递 264 16.4 静态重载的解决方法 265推荐阅读 266习题 266第17章 数据流分析 26917.1 流分析使用的中间表示 27017.2 各种数据流分析 27117.2.1 到达定值 27117.2.2 可用表达式 27317.2.3 到达表达式 27417.2.4 活跃分析 27417.3 使用数据流分析结果的几种转换 274 17.3.1 公共子表达式删除 27417.3.2 常数传播 27517.3.3 复写传播 27517.3.4 死代码删除 27517.4 加快数据流分析 27617.4.1 位向量 27617.4.2 基本块 27617.4.3 结点排序 27717.4.4 使用-定值链和定值-使用链 277 17.4.5 工作表算法 27817.4.6 增量式数据流分析 27817.5 别名分析 28117.5.1 基于类型的别名分析 28217.5.2 基于流的别名分析 28317.5.3 使用可能别名信息 28417.5.4 严格的纯函数式语言中的别名分析 285推荐阅读 285习题 285第18章 循环优化 28718.1 必经结点 28918.1.1 寻找必经结点的算法 28918.1.2 直接必经结点 28918.1.3 循环 29018.1.4 循环前置结点 29118.2 循环不变量计算 29218.3 归纳变量 29318.3.1 发现归纳变量 29418.3.2 强度削弱 29518.3.3 删除 29618.3.4 重写比较 29618.4 数组边界检查 29718.5 循环展开 300推荐阅读 301习题 301第19章 静态单赋值形式 30319.1 转化为SSA形式 30519.1.1 插入φ函数的标准 30619.1.2 必经结点边界 30619.1.3 插入φ函数 30819.1.4 变量重命名 30919.1.5 边分割 31019.2 必经结点树的高效计算 31019.2.1 深度优先生成树 31019.2.2 半必经结点 31119.2.3 Lengauer-Tarjan算法 31219.3 使用SSA的优化算法 31519.3.1 死代码删除 31519.3.2 简单的常数传播 31619.3.3 条件常数传播 31719.3.4 保持必经结点性质 31919.4 数组、指针和存储器 32019.5 控制依赖图 32119.6 从SSA形式转变回来 32319.7 函数式中间形式 324推荐阅读 327习题 328第20章 流水和调度 33120.1 没有资源约束时的循环调度 33220.2 有资源约束的循环流水 33620.2.1 模调度 33720.2.2 寻找最小的启动间距 33820.2.3 其他控制流 34020.2.4 编译器应该调度指令吗 34020.3 分支预测 34120.3.1 静态分支预测 34220.3.2 编译器应该预测分支吗 342推荐阅读 343习题 343第21章 存储层次 34621.1 cache的组织结构 34621.2 cache块对齐 34921.3 预取 35021.4 循环交换 35421.5 分块 35521.6 垃圾收集和存储层次 357推荐阅读 358习题 358附录 Tiger语言参考手册 360参考文献 368索引 376下载后 点击此处查看更多内容。
c类的继承和多态例子
c类的继承和多态例子继承是面向对象编程中的重要概念之一,它允许一个类“继承”另一个类的属性和方法。
在C++中,继承分为三种类型:公有继承、私有继承和保护继承。
其中,公有继承是最常用的一种方式,也是实现多态的基础。
本文将通过一个例子来介绍C++中的公有继承和多态特性。
假设我们要设计一个动物园的系统,其中包含不同类型的动物。
首先,我们定义一个基类Animal,代表所有动物的共有属性和方法。
然后,派生出几个具体的动物类,如Lion(狮子)、Elephant (大象)和Monkey(猴子),它们都是Animal类的派生类。
1. 基类Animal的定义:```c++class Animal {public:Animal() {} // 构造函数virtual ~Animal() {} // 虚析构函数virtual void move() const = 0; // 纯虚函数,用于表示不同动物的移动方式protected:int age; // 年龄double weight; // 体重};```2. 派生类Lion的定义:```c++class Lion : public Animal {public:Lion(int a, double w) : Animal(), color("yellow") { age = a;weight = w;}void move() const {std::cout << "Lion is running." << std::endl;}private:std::string color; // 颜色};```3. 派生类Elephant的定义:```c++class Elephant : public Animal {public:Elephant(int a, double w) : Animal(), height(3.5) { age = a;weight = w;}void move() const {std::cout << "Elephant is walking." << std::endl; }private:double height; // 身高};```4. 派生类Monkey的定义:```c++class Monkey : public Animal {public:Monkey(int a, double w) : Animal(), num_bananas(5) {age = a;weight = w;}void move() const {std::cout << "Monkey is jumping." << std::endl;}private:int num_bananas; // 香蕉数目};```以上就是实现动物园系统的基本类定义。
C#课件 构造函数和多态
12
构造函数与其他方法的区别
1.构造函数的命名必须和类名完全相同; 1.构造函数的命名必须和类名完全相同;而一般方法则不 构造函数的命名必须和类名完全相同 能和类名相同. 能和类名相同. 2.构造函数的功能主要用于在类的对象创建时定义初始 2.构造函数的功能主要用于在类的对象创建时定义初始 化的状态.它没有返回值,也不能用void来修饰. void来修饰 化的状态.它没有返回值,也不能用void来修饰.这就保证 了它不仅什么也不用自动返回, 了它不仅什么也不用自动返回,而且根本不能有任何选择 而其他方法都有返回值.即使是void返回值. void返回值 .而其他方法都有返回值.即使是void返回值. 3.构造函数不能被直接调用,必须通过new运算符在创建 3.构造函数不能被直接调用,必须通过new运算符在创建 构造函数不能被直接调用 new 对象时才会自动调用, 对象时才会自动调用,一般方法在程序执行到它的时候被 调用. 调用. 4.静态构造函数只能对静态数据成员进行初始化 静态构造函数只能对静态数据成员进行初始化, 4.静态构造函数只能对静态数据成员进行初始化,而不 能对非静态数据成员进行初始化。但是, 能对非静态数据成员进行初始化。但是,非静态构造函 数既可以对静态数据成员赋值, 数既可以对静态数据成员赋值,也可以对非静态数据成 员进行初始化。 员进行初始化。
7
静态构造函数 (1)用于对静态字段 只读字段等的初始化; 用于对静态字段、 (1)用于对静态字段、只读字段等的初始化;
(2)添加static关键字,不能添加访问修饰符, (2)添加static关键字,不能添加访问修饰符,因为静态 添加static关键字 构造函数都是私有的; 构造函数都是私有的; (3)类的静态构造函数在给定应用程序域中至多执行一次 类的静态构造函数在给定应用程序域中至多执行一次, (3)类的静态构造函数在给定应用程序域中至多执行一次, 只有创建类的实例或者引用类的任何静态成员才激发, 只有创建类的实例或者引用类的任何静态成员才激发,不能 带又参数; 带又参数; (4)静态构造函数是不可继承的 而且不能被直接调用; 静态构造函数是不可继承的, (4)静态构造函数是不可继承的,而且不能被直接调用; (5)如果类中包含用来开始执行的 方法, (5)如果类中包含用来开始执行的 Main 方法,则该类的 方法之前执行. 静态构造函数将在调用 Main 方法之前执行.任何带有初始值 设定项的静态字段,则在执行该类的静态构造函数时, 设定项的静态字段,则在执行该类的静态构造函数时,先要 按照文本顺序执行那些初始值设定项; 按照文本顺序执行那些初始值设定项; (6)如果没有编写静态构造函数 如果没有编写静态构造函数, (6)如果没有编写静态构造函数,而这时类中包含带有初 始值设定的静态字段, 始值设定的静态字段,那么编译器会自动生成默认的静态构 造函数; 造函数; 一个类可以同时拥有实例构造函数和静态构造函数, 一个类可以同时拥有实例构造函数和静态构造函数,这 是惟一可以具有相同参数列表的同名方法共存的情况。 是惟一可以具有相同参数列表的同名方法共存的情况。
C 实验多态性实验报告
class Point { public:
Point(int xx,int yy):x(xx),y(yy) {} void display()const; Point &operator++(); Point operator++(int); Point &operator--(); Point operator--(int); private:
using namespace std;
int Double(int x);
long Double(long x);
float Double(float x);
double Double(double x);
int main()
{ int myInt = 6500;
cout<<Double(myInt)<<endl;
学习使用虚函数实现动态多态性。而虚函数就是在基类中被关键字 virtual 说明,
实 并在派生类中重新定义的函数,且在派生类中重工业新定义时,函数原型,包括返回
类型、函数名、参数个数与参数类型的顺序,都必须与基类中的完全相同。此外,构 验
造函数不能是虚函数,但析构函数可以是虚函数。
总
函数的重载方法有一参数个数相同,但是类型不同;二参数个数不同;三 coust
实
验 Visual C++的编译环境下,独立完成实验要求的内容,独立完成编写、编译以及运行
原
的过程
理
实
验 安装了 Visual C++的 PC 机器
仪
器
生活实例解释面向对象的多态
面向对象的多态什么是多态在面向对象编程中,多态(Polymorphism)是一个重要的概念。
它指的是同一个方法可以根据不同的对象产生不同的行为。
换句话说,多态允许我们使用统一的接口来处理不同类型的对象。
多态是面向对象编程的三大特性之一,其余两个特性是封装和继承。
封装是指将数据和方法包装在一个对象中,继承是指一个类可以继承另一个类的属性和方法。
多态使得程序更加灵活,可扩展性更强。
生活中的多态示例为了更好地理解多态,我们可以通过生活中的一些例子来解释它。
1. 动物发声想象一下,你在一座动物园里散步。
你经过了一些动物的笼子,每个动物都在发出自己独特的声音。
狗在汪汪叫,猫在喵喵叫,鸟在唧唧鸣。
尽管它们发出的声音不同,但它们都是动物,都具有发声的能力。
在这个例子中,动物园可以被看作是一个类,而每个动物则是该类的实例。
类定义了动物的共同属性和方法,而每个实例则可以根据自己的特性来表现不同的行为。
这就是多态的体现。
2. 图形的面积计算假设你正在画一些图形,包括圆形、矩形和三角形。
你需要计算每个图形的面积。
不同的图形有不同的计算公式,但它们都有一个共同的方法——计算面积。
在这个例子中,每个图形可以被看作是一个类,而计算面积的方法则是这个类的一个公共方法。
每个图形类可以根据自己的特性实现不同的计算面积的方式。
当你调用计算面积的方法时,程序会根据具体的图形类型来执行相应的计算。
多态的实现方式在面向对象编程中,实现多态有两种常见的方式:继承和接口。
1. 继承实现多态继承是面向对象编程中的一个重要概念,它允许一个类继承另一个类的属性和方法。
通过继承,子类可以重写父类的方法,从而实现多态。
以动物为例,我们可以定义一个基类 Animal,它有一个方法叫做 makeSound()。
然后我们定义几个子类,如 Dog、Cat 和 Bird,它们分别重写了 makeSound() 方法来发出不同的声音。
class Animal {public void makeSound() {System.out.println("Animal makes sound");}}class Dog extends Animal {@Overridepublic void makeSound() {System.out.println("Dog barks");}}class Cat extends Animal {@Overridepublic void makeSound() {System.out.println("Cat meows");}}class Bird extends Animal {@Overridepublic void makeSound() {System.out.println("Bird chirps");}}public class Main {public static void main(String[] args) {Animal dog = new Dog();Animal cat = new Cat();Animal bird = new Bird();dog.makeSound(); // 输出:Dog barkscat.makeSound(); // 输出:Cat meowsbird.makeSound(); // 输出:Bird chirps}}在上面的例子中,我们定义了一个 Animal 类作为基类,然后定义了三个子类 Dog、Cat 和 Bird,它们都重写了 makeSound() 方法。
1在C中多态性体现在哪几方面?
1 在C++ 中多态性体现在哪几方面?答案:在C++中,多态性体现在两个方面:编译多态性——对同一消息的不同操作是在程序编译时就确定了,即静态多态性。
运行多态性——对同一消息的不同操作是在程序运行时根据不同的运行状况才确定,即动态多态性。
2 函数重载与虚函数有哪些相同点与不同点?答案:函数重载与虚函数之间的相同点是多个函数版本具有相同的函数名,即表现出对同一消息的不同操作。
而二者之间的不同点表现在:⑴函数重载的不同函数版本既允许存在于同一类中,也允许基类的成员函数在派生类中重载。
存在于同一类中的不同重载函数版本的参数(类型、个数、顺序)必须有所不同。
如果存在于基类和派生类中的成员函数的原型完全一致,则派生类的成员函数将覆盖基类的同名函数。
⑵虚函数是用来表现基类和公有派生类的相同原型成员函数之间的关联关系的实现机制,因此这种同原型成员函数必须分属于基类和派生类,并且首先在基类中用关键字virtual 声明虚函数;一个虚函数一旦被定义,就可以在该基类的一个或多个直接或间接派生类中被重新定义;虚函数重新定义时,其函数原型,即包括返回类型、函数名、参数的类型、个数和顺序,都必须与基类中的原型完全一致。
3 虚函数是如何实现面向对象系统的多态性的,它会带来什么益处?答案:在一个具有公有派生关系的类层次结构中,只要在基类中将某个接口函数声明为虚函数,并在该基类的直接和间接公有派生类中重新定义该虚函数的不同新版本,就可以实现在程序运行期间,使用一个基类指针动态地指向基类和从该基类直接或间接派生的任何类的对象,并通过该指针调用虚函数在不同类中定义的不同版本,即动态多态性。
显然,虚函数为面向对象系统提供了一种更为灵活的多态性,这种多态能力对于期望在基类中为从该基类派生的所有类定义统一的操作接口的设计尤为重要。
4 下面有两段程序,判断它们是否正确,若有错误,给予纠正。
①class base{// …public:virtual void show();};class derive : public base{// …public:void show();};main(){base obj1, obj2, *ptr1;derive obj3, obj4, *ptr2;ptr1 = &obj1;ptr1->show();ptr1 = &obj3;ptr1->show();ptr2 = &obj4;ptr2->show();ptr2 = &obj2;ptr2->show();// …}②class fruit{// …public:virtual void show() = 0;// …};class apple : public fruit{// …public:void show();// …};main(){fruit fru, *ptr;apple app;ptr = &app;ptr->show();ptr = &fru;ptr->show();// …}答案:①该程序的main()中出现了使用派生类指针指向基类对象,并通过该指针调用基类虚函数的错误语句(ptr2 = &obj2; ptr2->show();),因此,应该将main()修改为:main(){base obj1, obj2, *ptr1;derive obj3, obj4, *ptr2;ptr1 = &obj1;ptr1->show();ptr1 = &obj3;ptr1->show();ptr2 = &obj4;或ptr1 = &obj4;ptr2->show(); 或ptr1->show();// …}②该程序的main()中出现了为抽象类fruit创建对象,并通过基类指针调用纯虚函数的错误语句(fruit fru, ptr = &fru; ptr->show();),因此,应该将main()修改为:main(){fruit *ptr;apple app;ptr = &app;ptr->show();// …}5 判断下列各段程序是否可以通过编译?为什么?⑴#include <iomanip.h>class X{public:X() { }virtual void foo();};class Y : public X{public:Y() { }void foo() { cout << “Y’s foo invoked” << endl; }};int main() { return 0; }答案:由于基类中的虚函数没有定义实现代码,所以不能通过编译。
《C++程序设计》(谭浩强版)
{
//主函数体开始 int a,b,m; //变量声明 cin>>a>>b; //输入变量a和b的值 m=max(a,b); //调用max函数,将得到的值赋给m cout<<″max=″<<m<<′\\n′; //输出大数m的值 return 0; //如程序正常结束,向操作系统返回一个零值 } //主函数结束
本程序包括两个函数:主函数main和被调用的函数 max。 程序运行情况如下: 18 25 ↙ (输入18和25给a和b) max=25 (输出m的值) 注意输入的两个数据间用一个或多个空格间隔,不 能以逗号或其他符号间隔。
在上面的程序中,max函数出现在main函数之前, 因此在main函数中调用max函数时,编译系统能识 别max是已定义的函数名。如果把两个函数的位臵 对换一下,即先写main函数,后写max函数,这时 在编译main函数遇到max时,编译系统无法知道 max代表什么含义,因而无法编译,按出错处理。 为了解决这个问题,在主函数中需要对被调用函数 作声明。上面的程序可以改写如下:
#include <iostream>是一个“包含命令”,它的作 用是将文件iostream的内容包含到该命令所在的程 序文件中,代替该命令行。文件iostream的作用是 向程序提供输入或输出时所需要的一些信息。 iostream是i-o-stream 3个词的组合,从它的形式就 可以知道它代表“输入输出流”的意思,由于这类 文件都放在程序单元的开头,所以称为“头文件” (head file)。在程序进行编译时,先对所有的预 处理命令进行处理,将头文件的具体内容代替 #include命令行,然后再对该程序单元进行整体编 译。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
例13.5在类中,用字符数组实现字符串。
#include <iostream.h>#include<string.h>class Student{char Num[10]; //学号,注意:用数组实现char Name[10]; //姓名,注意:用数组实现int Score; //成绩public:Student(char num[ ]=NULL, char name[ ]=NULL, int score=0){ if(num)strcpy(Num, num);else Num[0]='\0';if(name)strcpy(Name, name);else Name[0]='\0';Score=score;}void Show( ){ cout<<"Num="<<Num<<'\t'<<"Name="<<Name<<'\t'<<"Score="<<Score<<endl;}};void main( ){ Student stud1("01201", "Mary", 88), stud2;stud2=stud1; //Astud1.Show( );stud2.Show( );}程序的运行结果是:Num=01201 Name=Mary Score=88 Num=01201 Name=Mary Score=88返回ppt讲稿例13.7 += 运算符重载函数的返回值为void#include <iostream.h>class Complex{double Real, Image;public:Complex(double r=0, double i=0){ Real=r; Image=i; }void operator+=(const Complex &c)//返回值为void型{Real+=c.Real;Image+=c.Image;}void Show( ){cout<<Real;if(Image>0) cout<<'+'<<Image<<'i'; //如果是正数,必须输出正号else if(Image<0) cout<<Image<<'i'; //如果是负数,自动输出负号cout<<endl;}};void main( ){Complex c1(2, 3), c2(4, -2);cout<<"c1=";c1.Show( );cout<<"c2=";c2.Show( );c1+=c2; // Acout<<"c1+=c2;c1=";c1.Show( );}运行结果如下:c1=2+3ic2=4-2ic1+=c2;c1=6+1i程序中的A行被处理成c1. operator+=(c2)。
如果有连续赋值的表达式,如c1+=c2+=c3,编译器会处理成c1. operator+=( c2. operator+=(c3) ),注意划线部分是第一次赋值,表达式的结果是void类型,此时若欲做第二个赋值就不行了。
将上例中的operator+=( )重载函数改写成:Complex operator+=(const Complex &c)// 返回值为本类类型的对象{Real+=c.Real;Image+=c.Image;return *this;}此时处理c1+=c2+=c3就没有问题了。
返回ppt讲稿例13.8 对于字符串类,重载= 运算符,返回对象自身的引用。
就本例而言,可以不定义拷贝构造函数,程序能正确运行。
#include <iostream.h>#include <string.h>class String{ char *Strp;public:String(char *s=0) // 构造函数{ if(s){ Strp=new char[strlen(s)+1];strcpy(Strp, s);}else Strp=0;}String & operator = (String & s) // A赋值运算符重载函数{ if(Strp) delete [ ]Strp;if(s.Strp){ Strp=new char[strlen(s.Strp)+1];strcpy(Strp, s.Strp);}else Strp=0;return * this;}~String( ) // 析构函数{ if(Strp) delete [ ]Strp; }void Show( ){ cout<<Strp<<endl; }};void main( ){ String str1("Sample String1");String str2;str2=str1; // 对象赋值运算str2.Show( ); // 输出Sample String1}但是若将程序中A行的函数头部改为:String operator = (String & s) ,即返回本类对象,因类中有动态申请的字符串空间,则必须增加定义拷贝构造函数。
因为要使用返回的对象去初始化内存临时对象,所以应在类体中增加定义拷贝构造函数如下:String(String & s) // 拷贝构造函数{ if(s.Strp){ Strp=new char[strlen(s.Strp)+1];strcpy(Strp, s.Strp);}else Strp=0;}返回ppt讲稿例13.11成员函数和类型转换函数的比较#include <iostream.h>class RMB //定义一个“人民币”类{int Yuan, Jiao, Fen; //元、角、分public:RMB(int y=0, int j=0, int f=0){ Y uan=y; Jiao=j; Fen=f; }operator int( ) //转换成整数值(结果单位:分) { return (Yuan*100+Jiao*10+Fen); }operator double( ) //转换成实数值(结果单位:元) { return (Yuan+double(Jiao)/10+double(Fen)/100); } int GetFen( ) //用成员函数转换成“分”值{ return (Yuan*100+Jiao*10+Fen); }};void main(void){ RMB r(23, 8, 6);int r1, r2, r3;r1=r ; //A 处理成r1 = int(r);本质与B行一致r2=int(r); //B 处理成r2 = r.operator int( );r3=r.GetFen( ); //C 调用成员函数得到分值cout<<"r1="<<r1<<endl;cout<<"r2="<<r2<<endl;cout<<"r3="<<r3<<endl;double d;d = r; //D 处理成d = r.operator double( ); cout<<"d="<<d<<endl;}程序的运行结果如下:r1=2386r2=2386r3=2386d=23.86返回ppt讲稿假定一种货币只有元和角两种单位,一元等于10角,++和-- 表示加减1角。
定义一个类Money 用于实现++和-- 运算。
例13.12实现++和--的前置和后置运算符重载#include <iostream.h> ++ - - 表示家减1角class Money{int Yuan, Jiao; //元、角public:Money(int y=0, int j=0){ Y uan=y; Jiao=j; }Money(double d){ Y uan=int(d); Jiao=int((d-Yuan)*10); }Money operator++( ); //①用成员函数实现前置++ Money operator++(int); //②用成员函数实现后置++ friend Money operator--(Money &m);//③用友元函数实现前置- - friend Money operator--(Money &m, int);//④用友元函数实现后置- - void Show(char *s){cout<<s<<':'<<Yuan<<","<<Jiao<<endl;}};Money Money::operator++( )//①用成员函数实现前置++ {Jiao++;if(Jiao>=10){Yuan+=1;Jiao-=10;}return *this;}Money Money::operator++(int)//②用成员函数实现后置++{Money temp = *this; //保存对象原始的值 Jiao++;if(Jiao>=10){Yuan+=1;Jiao-=10;}return temp; //返回对象原始的值}Money operator--(Money &m)//③用友元函数实现前置--{if(m.Jiao==0){m.Yuan -= 1;m.Jiao=10; } m.Jiao--; return m;}Money operator--(Money &m, int)//④用友元函数实现后置--{Money temp=m; //保存对象原始的值 if(m.Jiao==0){ m.Yuan-=1; m.Jiao=10; }m.Jiao--;return temp; //返回对象原始的值 }void main(void){Money m(15, 3), m1, m2, m3, m4;cout<<"m: "; m.Show("m");m1 = ++m;cout<<"m1=++m;\n";m1.Show("m1"); m.Show("m");m2=m++;cout<<"m2=m++;\n";m2.Show("m2"); m.Show("m");m3=--m;cout<<"m3=--m;\n"; m3.Show("m3"); m.Show("m");m4=m--;cout<<"m4=m--;\n"; m4.Show("m4"); m.Show("m");Money m5(8.5);m5.Show("m5");}返回ppt讲稿例13.18定义字符串类String,并测试重载的运算符以及成员函数//Li1318.h#include <iostream.h>#include <string.h>class String{protected:int Length; //字符串的长度char *Strp; //指向字符串的指针public:String( ){ Strp=NULL; Length=0; } //缺省构造函数String(const char *s); //构造函数,以字符指针作为参数String(const String &);//拷贝构造函数,以字符串对象的引用作为参数~String( ){ if(Strp) delete [ ] Strp; }const char *IsIn( const char ) const; //返回参数字符在字符串中第一次出现的地址int IsSubStr(const char *) const; //判断参数字符串是否为子串void Show( ) //输出字符串{ cout << Strp << '\n'; }int GetLen( ) { return Length; } //返回字符串的长度const char * GetString( ) { return Strp; } //取字符串首指针operator const char* ( ) const // A 类型转换函数,返回指向常量的字符串指针{ return (const char *) Strp; }String & operator = (String &); //重载赋值运算符friend String operator+(const String &, const String & ); //友元实现+重载friend String operator-(const String &, const char * );//友元实现-重载int operator < ( const String &) const;int operator > ( const String &) const;int operator == ( const String &) const;};String::String(const char *s) //构造函数,以字符指针作为参数{ if(s){ Length = strlen(s);Strp = new char[Length+1];strcpy(Strp, s);}else{ Strp = NULL; Length = 0; }}String::String(const String &s) //拷贝构造函数,以字符串对象的引用作为参数{ Length = s.Length;if(s.Strp){ Strp = new char[Length+1];strcpy(Strp, s.Strp);}else Strp = NULL;}const char *String::IsIn( const char c) const // B判断字符c是否在字符串中, 如果在,返回其第一次出现时的地址, 否则返回空指针NULL{ char *p=Strp;while(*p)if(*p++ == c) return --p;return NULL;}int String::IsSubStr(const char *s) const //判断s所指向的字符串是否为类中字符串的子串。