第五章 多态性和虚函数

合集下载

虚函数与多态性实验

虚函数与多态性实验

一.实验目的及要求1.进一步熟悉类的设计、运用继承与派生机制设计派生类,合理设置数据成员和成员函数。

2.掌握通过继承、虚函数、基类的指针或引用实现动态多态性的方法。

3.理解并掌握具有纯虚函数的抽象类的作用,在各派生类中重新定义各纯虚函数的方法,以及此时实现的动态多态性。

二.实验内容在自己的文件夹下建立一个名为exp5的工程,在该工程中做如下操作:定义一个抽象类容器类,其中定义了若干纯虚函数,实现求表面积、体积、输出等功能。

由此抽象类派生出正方体、球体和圆柱体等多个派生类,根据需要定义自己的成员变量,在各个派生类中重新定义各纯虚函数,实现各自类中相应功能,各个类成员的初始化均由本类构造函数实现。

(1)在主函数中,定义容器类的指针和各个派生类的对象,使指针指向不同对象处调用相同的函数能执行不同的函数代码,从而实现动态多态性。

(2)定义一个顶层函数void TopPrint(Container &r);使得主函数中调用该函数时,根据实在参数所有的类自动调用对应类的输出函数。

(3)主函数中定义一个Container类对象,观察编译时的错误信息,从而得出什么结论三.实验程序及运行结果#include <iostream>using namespace std;class Base{public:virtual void f(){ cout << "调用Base::f()" << endl; }};class Derived: public Base{public:void f(){ cout << "调用Derived::f()" << endl; } // 虚函数};int main(void){Derived obj; // 定义派生类对象Base *p = &obj; // 基类指针p->f(); // 调用函数f()system("PAUSE");return 0;}2.#include <iostream>using namespace std; //class Base{public:virtual void Show() const{ cout << "调用Base::Show()" << endl; } // 虚函数};class Derived: public Base{public:void Show() const{ cout << "调用Derived::Show()" << endl; }};void Refers(const Base &obj) // 基类引用{obj.Show(); // 调用函数Show()}int main(void){Base obj1;Derived obj2; // 定义对象Refers(obj1); // 调用函数Refers()Refers(obj2);system("PAUSE");return 0;}3.#include <iostream>using namespace std; /class Base{private:int m;public:Base(int a): m(a){ }virtual void Show() const{ cout << m << endl; }// 虚函数};class Derived: public Base{private:int n;public:Derived(int a, int b): Base(a), n(a){ } // 构造函数void Show() const{cout << n << ","; /Base::Show(); // 调用基类的Show() }};int main(void){Base obj1(168);Derived obj2(158, 98);Base *p;p = &obj1;p->Show();p = &obj2;p->Show();p->Base::Show();system("PAUSE");return 0;}4.#include <iostream>using namespace std;class A{public:virtual void Show() const{ cout << "基类A" << endl; } };class B: public A{public:void Show() const{ cout << "派生类B" << endl; } };int main(void){B obj;A *p = &obj;p->Show();system("PAUSE");return 0;}5.#include <iostream>using namespace std;const double PI = 3.1415926;class Shape{public:virtual void Show() const = 0;static double sum;};class Circle: public Shape{private:double radius;public:Circle(double r): radius(r){ sum += PI * radius * radius; }void Show() const{cout << "圆形:" << endl;cout << "半径:" << radius << endl;cout << "面积:" << PI * radius * radius << endl;}};class Rectangle: public Shape{private:double height;double width;public:Rectangle(double h, double w): height(h), width(w){ sum += height * width; }void Show() const{cout << "矩形:" << endl;cout << "高:" << height << endl;cout << "宽:" << width << endl;cout << "面积:" << height * width << endl;}};double Shape::sum = 0;int main(void){char flag = 'Y'; 'Shape *p;while (toupper(flag) == 'Y'){cout << "请选择输入类别(1.圆形2.矩形)";int select;cin >> select;switch (select){case 1:double r;cout << "输入半径:";cin >> r;p = new Circle(r);p->Show();delete p;break;case 2:double h, w;cout << "输入高:";cin >> h;cout << "输入宽:";cin >> w;p = new Rectangle(h, w);p->Show(); // 显示相关信息delete p; // 释放存储空间break;default: // 其它情况, 表示选择有误cout << "选择有误!"<< endl;break;}cout << endl << "是否继续录入信息?(Y/N)";cin >> flag;}cout << "总面积:" << Shape::sum << endl;system("PAUSE");return 0;}6.#include <iostream>using namespace std;const double PI = 3.1415926;const int NUM = 10;class Shape{public:virtual void ShowArea() const = 0;static double sum;};class Circle: public Shape{private:double radius;public:Circle(double r): radius(r){ sum += PI * radius * radius; }void ShowArea() const{ cout << "圆面积:" << PI * radius * radius << endl; }};class Rectangle: public Shape{private:double height;double width;public:Rectangle(double h, double w): height(h), width(w){ sum += height * width; }void ShowArea() const{ cout << "矩形面积:" << height * width << endl; }};class Square: public Shape{private:double length;public:Square(double a): length(a){ sum += length * length; }void ShowArea() const{ cout << "正方形面积:" <<length * length << endl; } };double Shape::sum = 0;int main(void){Shape *shape[NUM];int count = 0;while (count < NUM){cout << "请选择(1.圆形2.矩形3.正方形4.退出):";int select;cin >> select;if (select == 4) break;switch (select){case 1:double r;cout << "输入半径:";cin >> r;shape[count] = new Circle(r);shape[count]->ShowArea();count++;break;case 2:double h, w;cout << "输入高:";cin >> h;cout << "输入宽:";cin >> w;shape[count] = new Rectangle(h, w);shape[count]->ShowArea();count++;break;case 3:double a;cout << "输入边长:";cin >> a;shape[count] = new Square(a);shape[count]->ShowArea();count++;break;default:cout << "选择有误!"<< endl;break;}}cout << "总面积:" << Shape::sum << endl;for (int i = 0; i < count; i++) delete shape[i];system("PAUSE");return 0;}五.实验总结通过本次试验 我更深刻的理解了某些语句如何使用及结构体的优点 也能更加熟练的编写简单的程序了 我深知实践要比书本更加重要 今后还要多练习 在实践中学习。

虚函数的多态性机制分析

虚函数的多态性机制分析
I N 0 9 3 4 SS 1 0 - 0 4
E ma : s @c c .e C — i xj c c t n l l n .
ht /www. z .e .n t / p: dn sn tc
C m u r n we g n e h o g o p t o l ea dT c n l y电脑 知 识 与技术 eK d o

1面 向对 象 程 序 设 计 的 多 态 性
在C+ , + 中 把类 看 作 类 型 , 以 p bi 式 继 承 的派 生 类 看 作是 基 类 的 子类 型 。从 而 产生 了多 态 对象 的类 型 不但 可 以是 派生 类 , ) 也可 以是 基类 , 也就是 说 , 个对 象 可 以 属 于 多种 类 型 。 一 2 基 类 的指 针 可 以 指 向基 类 对 象 , 可 以 指 向派 , 类 对象 ; ) 也 } - 基类 的 引用 可 以引 用 基 类 对 象 , 可 以引 用 派 生类 对 象 即一 个 对 也
象标 识 符 可 以属 于 多 种类 型 , 可 以标 识 多种 对 象 它
3 一 个 消息 可 以发 送 到 基 类 对象 , 可 以发 送 到 派 生类 刘 象 , 而 可 能 会得 到 不 同 的 解 释 , ) 也 从 引起 不 同的 动作 , 产生 不 同的 效果
C + 以 支持 两 种 多 态性 : +可 静态 多 态 性 和动 态 多 态 性 我们 知 道 , 序 在 运 行 前 的编 译 连 接 阶段 会 根据 源 程 序 中的 要 调 用 的 函 程
Abtat I jc— i ne rga migtelhfac eettig o r ci l hn me o el r . th oy rh s c: nOb t Or tdPorm n , n et erfc n s f ea hc e o n ni r l b e P l r e e h in l sh h r a p n a wo d u t mop —

c++多态性与虚函数习题答案

c++多态性与虚函数习题答案

多态性与虚函数1.概念填空题1.1 C++支持两种多态性,分别是编译时和运行时。

1.2在编译时就确定的函数调用称为静态联编,它通过使用函数重载,模板等实现。

1.3在运行时才确定的函数调用称为动态联编,它通过虚函数来实现。

1.4虚函数的声明方法是在函数原型前加上关键字virtual。

在基类中含有虚函数,在派生类中的函数没有显式写出virtual关键字,系统依据以下规则判断派生类的这个函数是否是虚函数:该函数是否和基类的虚函数同名;是否与基类的虚函数参数个数相同、类型;是否与基类的虚函数相同返回类型。

如果满足上述3个条件,派生类的函数就是虚函数。

并且该函数覆盖基类的虚函数。

1.5 纯虚函数是一种特别的虚函数,它没有函数的函数体部分,也没有为函数的功能提供实现的代码,它的实现版本必须由派生类给出,因此纯虚函数不能是友元函数。

拥有纯虚函数的类就是抽象类类,这种类不能实例化。

如果纯虚函数没有被重载,则派生类将继承此纯虚函数,即该派生类也是抽象。

3.选择题3.1在C++中,要实现动态联编,必须使用(D)调用虚函数。

A.类名B.派生类指针C.对象名D.基类指针3.2下列函数中,不能说明为虚函数的是(C)。

A.私有成员函数B.公有成员函数C.构造函数D.析构函数3.3在派生类中,重载一个虚函数时,要求函数名、参数的个数、参数的类型、参数的顺序和函数的返回值(A)。

A.相同B.不同C.相容D.部分相同3.4当一个类的某个函数被说明为virtual时,该函数在该类的所有派生类中(A)。

A.都是虚函数B.只有被重新说明时才是虚函数C.只有被重新说明为virtual时才是虚函数D.都不是虚函数3.5(C)是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。

A.虚析构函数B.虚构造函数C.纯虚函数D.静态成员函数3.6 以下基类中的成员函数,哪个表示纯虚函数(C)。

A.virtual void vf(int);B.void vf(int)=0;C.virtual void vf( )=0;D.virtual void vf(int){ }3.7下列描述中,(D)是抽象类的特性。

【C++面向对象的程序设计】6多态性

 【C++面向对象的程序设计】6多态性

虚析构函数
析构函数的作用是对象撤销之后清理现场。 在派生类对象撤销时,一般先调用派生类的 析构函数。再调用基类的析构函数。
然而,当定义的是一个指向基类的指针变量, 使用new运算符建立临时对象时,如果基类 中有析构函数,则在使用delete析构时只会 调用基类的析构函数。
这就需要将基类中的析构函数声明为虚函数。
虚函数的声明与使用
声明虚函数的一般格式如下: virtual 函数原型;
⑴ 必须首先在基类中声明虚函数。 ⑵ 派生类中与基类虚函数原型完全相同的成员函 数,即使在说明时前面没有冠以关键字virtual也 自动成为虚函数。
声明虚函数
⑶ 只有非静态成员函数可以声明为虚函数。 ⑷ 不允许在派生类中定义与基类虚函数名字及参数 特征都相同,仅仅返回类型不同的成员函数。 编译时 出错。 ⑸ 系统把函数名相同但参数特征不同的函数视为不 同的函数。 ⑹ 通过声明虚函数来使用C++提供的多态性机制时, 派生类应该从它的基类公有派生。
构函数等内容。
本章内容
静态联编与动态联编 虚函数的声明与使用 纯虚函数和抽象类 虚析构函数
Hale Waihona Puke 静态联编与动态联编所谓联编(tinding),就是使一个计算机程序的不同部 分彼此关联的过程。
静态联编在编译阶段完成,因为所有联编过程都在程 序开始运行之前完成,因此静态联编也叫先前联编或早期 联编。
另一种情况编译程序在编译时并不确切知道应把发送 到对象的消息和实现消息的哪段具体代码联编在一起,而 是在运行时才能把函数调用与函数体联系在一起,则称为 动态联编。
动态联编的实现
C ++语言中的动态联编是通过使用虚函数表 (Virtual Function Table)来实现的,虚函数表也称 为v-表。

实验四 虚函数与多态性

实验四 虚函数与多态性

实验四虚函数与多态性实验目的和要求 :1 了解多态性在 C++ 中的体现。

2 掌握虚函数的应用。

3 了解抽象类。

实验内容:定义一个基类为哺乳动物类Mammal,其中有数据成员年龄、重量、品种,有成员函数move()、speak()等,以此表示动物的行为。

由这个基类派生出狗、猫、马、猪等哺乳动物,它们有各自的行为。

编程分别使各个动物表现出不同的行为。

要求如下:1、从基类分别派生出各种动物类,通过虚函数实现不同动物表现出的不同行为。

2、今有狗: CAIRN:3岁,3kg;DORE:4岁,2kg;猫: CAT:5 岁,4kg;马: HORSE,5岁,60kg;猪: PIG,2岁,45kg。

3、设置一个 Mammal 类数组,设计一个屏幕菜单,选择不同的动物或不同的品种,则显示出动物相对应的动作,直到选择结束。

4、对应的动作中要先显示出动物的名称,然后显示年龄、重量、品种、叫声及其他特征。

实验原理:1.多态性:多态是指同样的消息被不同类型的对象接受时导致不同的行为,所谓消息是指对类的成员函数的调用,而不同的行为是指不同的实现,也就是调用不同的函数。

多态性从实现的角度来讲可以划分为两类:编译时的多态性和运行时的多态性。

编译时的多态性是在编译的过程中确定同名操作的具体操作对象,也就是通过重载函数来实现的。

运行时的的多态性是在程序运行过程中才动态地确定操作所针对的具体对象,使用虚函数来实现的。

2.虚函数:虚函数是重载的另一种形式,它提供了一种更为灵活的多态性机制。

虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是运行时才决定如何动作,即所谓的“动态连接”。

虚函数成员的定义:virtual 函数类型函数名(形参表)3.(1)抽象类:抽象类是一种特殊的类。

抽象类是为了抽象和设计的目的而建立的。

一个抽象类自身无法实例化,也就是说无法定义一个抽象类的对象,只能通过继承机制,生成抽象类的非抽象派生类,然后再实例化。

多态性与虚函数实验报告

多态性与虚函数实验报告

多态性与虚函数实验报告实验目的:通过实验掌握多态性和虚函数的概念及使用方法,理解多态性实现原理和虚函数的应用场景。

实验原理:1.多态性:多态性是指在面向对象编程中,同一种行为或者方法可以具有多种不同形态的能力。

它是面向对象编程的核心特性之一,能够提供更加灵活和可扩展的代码结构。

多态性主要通过继承和接口来实现。

继承是指子类可以重写父类的方法,实现自己的特定行为;接口是一种约束,定义了类应该实现的方法和属性。

2.虚函数:虚函数是在基类中声明的函数,它可以在派生类中被重新定义,以实现多态性。

在类的成员函数前面加上virtual关键字,就可以将它定义为虚函数。

当使用基类指针或引用调用虚函数时,实际调用的是派生类的重写函数。

实验步骤:1. 创建一个基类Shape,包含两个成员变量color和area,并声明一个虚函数printArea(用于打印面积。

2. 创建三个派生类Circle、Rectangle和Triangle,分别继承Shape类,并重写printArea(函数。

3. 在主函数中,通过基类指针分别指向派生类的对象,并调用printArea(函数,观察多态性的效果。

实验结果与分析:在实验中,通过创建Shape类和派生类Circle、Rectangle和Triangle,可以实现对不同形状图形面积的计算和打印。

当使用基类指针调用printArea(函数时,实际调用的是派生类的重写函数,而不是基类的函数。

这就是多态性的实现,通过基类指针或引用,能够调用不同对象的同名函数,实现了对不同对象的统一操作。

通过实验1.提高代码的可扩展性和灵活性:通过多态性,可以将一类具有相似功能的对象统一管理,节省了代码的重复编写和修改成本,增强了代码的可扩展性和灵活性。

2.简化代码结构:通过虚函数,可以将各个派生类的不同行为统一命名为同一个函数,简化了代码结构,提高了代码的可读性和维护性。

3.支持动态绑定:通过运行时的动态绑定,可以根据对象的实际类型来确定调用的函数,实现了动态绑定和多态性。

第五章多态性

第五章多态性

{cout<<"thisistheclassofbase2!"<<endl;}
}; class derive:public base1, public base2 //定义派生类derive { public: void who() {cout<<"this is the class of derive!"<<endl;} };
5.2运通 运算符重载
友元 运算符重载
成员 运算符重载
5.2.1 普通运算符重载
运算符重载的定义: 类型名 operator@(参数表)
{
// 函数体
(要重载的运算符)
}
5.2.1 普通运算符重载
class complex { public: double real,imag; complex(double r=0,double i=0) { real=r; imag=i;} }; int main() { complex com1(1.1,2.2), com2(3.3,4.4), total; total=com1+com2; //错误 //… return 0; complex operator+(complex om1,complex om2) } { complex temp; temp.real=om1.real+om2.real; temp.imag=om1.imag+om2.imag; return temp; }
C++面向对象程序设计 LOGO
第五章 多态性
任课老师:刘 晶
电子邮箱 :liujing@
5.1 多态性概述
面向对象设计中多态性就是不同对象收到相同 的消息时,产生不同的动作。 C++中的多态性是指用一个名字定义不同的 函数,这些函数执行不同但又类似的操作,从 而可以使用相同的调用方式来调用这些具有不 同功能的同名函数。

第五讲——多态性

第五讲——多态性

例——复数的运算
class complex //复数类 { public: complex(double r=0.0,double i=0.0) //构造函数 { real=r; imag=i; } void display(); //显示复数的值 private: double real; double imag; };

一点预备知识
先看一段代码: int fun(int x, int y) { return x*2; } 尽管这样的用法是正确的,但大多数C和C++的编译器都会 给出一个警告,抱怨参数y在程序中没有使用。为了避免警告, C++允许声明一个无名形参,以告诉编译器存在某参数,调用 者需要为其传递一个实参,但是函数不会用到它。下面给出使 用了无名参数的C++函数代码: int fun(int x,int) { return x*2; }
//显式调用
这两种写法编译器都可以接受,都做相同的理解。这 是编译器对人们习惯用法的支持。
运算符成员函数的设计
运算符 使用形式 等价式
oprd1 . operator @(oprd2 ) oprd . operator @( ) oprd . operator @( 0 ) 双目运算符@ oprd1 @ oprd2 前臵单目@ 后臵单目@ @ oprd oprd @
C++语言程序设计
第五章 多态性
本章主要内容

多态性 运算符重载 类型转换 虚函数 纯虚函数和抽象类
多态的概念 “多态‖保证了正在执行的代码总是和对象的
类型保持一致,而且不是像C语言那样靠复杂 (不少书籍讲的多态是狭义的,仅指动态多态。而广义 的逻辑判断选择合适的代码,多态性使得面 的多态应包括静态多态。) 向对象的程序能自动选择合适的代码去执行。 动态多态是指发出同样的消息被不同类型的对象接收 于是用户就可以方便地为原有的程序增加新 的代码功能,这种可扩充性无论对于渐进式 时,有可能导致完全不同的行为。即,在用户不作任 开发还是软件维护都是非常有意义的事。 何干预的环境下,类的成员函数的行为能根据调用它 的对象类型自动作出适应性调整,而且调整是发生在 程序运行时。 这种特性是靠“类型兼容规则”支撑的。即可以用基 类的指针或引用来操作子孙类的对象们,就如同操作 多种类型似的。 多态是面向对象程序设计的重要特征之一。 是C++语言扩展性在“继承”之后的又一重大表现 。

虚函数

虚函数

运行结果: P1.area()=0 C1.area()=1256.64 Pp->area()=1256.64 Rp.area()=1256.64
第 9 章
多 态 性
程序解释: 程序16行在基类Point中,将area()声明为虚函数。 第39通过Point类对象P1调用虚函数area()。 第41通过circle类对象C1调用area(),实现静态联编,调用的是Circle类area()。 第44、46行分别通过Point类型指针与引用调用area(),由于area()为虚函数,此 时进行动态联编,调用的是实际指向对象的area()。
第 9 章
多 态 性
Pp->area()=0 Rp.area()=0
C + + 语 言 程 序 设 计 教 程
9.3.1 静态联编与动态联编
程序解释:
程序39行调用P1.area()显示了Point类对象P1的面积; 程序41行通过调用C1.area() 显示了Circle类对象C1的面积;由于在类Circle中重新定义了area(), 它覆盖了基类的 area(), 故通过C1.area()调用的是类Circle中的area(), 返回了正确结果。 依照类的兼容性,程序43行用一个point型的指针指向了Circle类的对象C1, 第44行通 过Pp->area()调用area(), 那么究竟调用的是哪个area(), C++此时实行静态联编,根据 Pp的类型为Point型,将从Point类中继承来的area()绑定给Pp, 因此,此时调用的是 Point派生的area(), 显示的结果为0。 同样,在第46行由引用Rp调用area()时,也进行静态联编,调用的是Point派生的 area(), 显示的结果为0。 显然,静态联编盲目根据指针和引用的类型而不是根据实际指向的目标确定调用的 函数,导致了错误。 动态联编则在程序运行的过程中,根据指针与引用实际指向的目标调用对应的函数 ,也就是在程序运行时才决定如何动作。虚函数(virtual function)允许函数调用与函 数体之间的联系在运行时才建立,是实现动态联编的基础。虚函数经过派生之后,可以 在类族中实现运行时的多态,充分体现了面向对象程序设计的动态多态性。

C++ 8多态与虚函数

C++ 8多态与虚函数
调用自身,无穷递归
fun(i);
8.6.1 虚函数的定义
【例8.5_1】根据赋值兼容规则可以用基类的指针指向 派生类对象,如果由该指针撤销派生类对象,则必须将析构函 数说明为虚函数,实现多态性,自动调用派生类析构函数。 通常要求将类设计成通用的,无论其他程序员怎样调用都 必须保证不出错,所以必须把析构函数定义为虚函数。 下面把【例8.5】析构函数改造为虚函数 class Person{ //数据成员略 public: virtual ~Person(); //只需在此声明一次,派生类的析构函数全为虚函数 }; //其他成员函数略
8.6.1 虚函数的定义
成员函数设置为虚函数的要点:
1. 派生类中定义虚函数必须与基类中的虚函数同名外,还必须 同参数表,同返回类型。否则被认为是重载,而不是虚函数。 如基类中返回基类指针,派生类中返回派生类指针是允许的, 这是一个例外。 静态类型:在编译时可知的引用类型或指针类型 2. 只有类的成员函数才能说明为虚函数。这是因为虚函数仅适 class Base; Base *p; //指针p的静态类型为Base 用于有继承关系的类对象。 动态类型:指针或引用所绑定的对象类型,仅运行时可知 3. 静态成员函数,是所有同一类对象共有,不受限于某个对象, class Derived:public Base; 不能作为虚函数。 Derived d; Base *p=&d; //指针p的动态类型为Derived 4. 一个类对象的静态和动态构造是相同的,实现动态多态性时, 必须使用基类类型的指针变量或引用,使该指针指向该基类的 不同派生类的对象,并通过该指针指向虚函数,才能实现动态 的多态性。
编译时的多态性 在C++ 中有两 种多态 性 运行时的 多态性

第7部分 多态性与虚函数

第7部分  多态性与虚函数

例: 虚函数成员
#include <iostream> using namespace std; class B0 {public: virtual void display() //虚成员函数 {cout<<"B0::display()"<<endl;} }; class B1: public B0 {public: void display(){cout<<"B1::display()"<<endl;} }; class D1: public B1 {public: void display(){cout<<"D1::display()"<<endl;} };
B0 <<virtual>>+display():void
B1 +display():void
D1 +display():void
包含虚成员函数的B0类及派生关系的UML图表示
实例讲解
#include <iostream.h> class Base { public: virtual void fun(int x){cout<<"base class, x="<<x<<endl;} }; 派生类的函数原型与基类中的函数原 class Derive:public Base 型不同(参数类型不同),所以不能 { 实现多态性。 public: void fun(float x){cout<<"derive class, x="<<x<<endl;} }; void globefun(Base &b) { int i=1; b.fun(i); float f=2.0; b.fun(f); } void main() { Base b; Derive d; globefun(b); globefun(d); }

虚函数的用法

虚函数的用法

虚函数的用法
虚函数是面向对象编程中的一个重要概念。

它允许子类重写父类中的同名函数,以实现多态性。

在C++中,使用关键字"virtual"来声明一个函数为虚函数。

虚函数的使用有以下几个关键点:
1. 多态性:虚函数的主要作用是实现多态性。

当一个指向父类的指针或引用调用一个虚函数时,实际执行的是子类中重写的函数。

这种行为允许在运行时根据对象的实际类型来确定调用的函数。

2. 动态绑定:虚函数的调用是动态绑定的,也就是说在运行时确定具体调用的函数。

与之相反的是静态绑定,它是在编译时确定调用的函数。

动态绑定使得程序具有更大的灵活性和扩展性。

3. 虚函数表:为了实现动态绑定,编译器会为每个包含虚函数的类创建一个虚函数表(vtable)。

虚函数表是一个存储函数指针的数组,每个函数指针指向对应虚函数的实际实现。

每个对象都有一个指向其类的虚函数表的指针,通过这个指针可以实现动态调用。

4. 纯虚函数:有时候父类中的虚函数并不需要有实际的实现,而只
是为了在子类中进行重写。

这种函数被称为纯虚函数,可以通过在函数声明中添加"= 0" 来表示。

包含纯虚函数的类被称为抽象类,它只能作为基类使用,不能被实例化。

综上所述,虚函数是实现多态性的关键机制之一。

通过在父类中声明虚函数并在子类中重写,可以实现基于对象实际类型的动态绑定,提高程序的灵活性和可扩展性。

虚函数继承,多态,虚继承(详细讲解)

虚函数继承,多态,虚继承(详细讲解)

虚函数的定义要遵循以下重要规则:1.如果虚函数在基类与派生类中出现,仅仅是名字相同,而形式参数不同,或者是返回类型不同,那么即使加上了virtual关键字,也是不会进行滞后联编的。

2.只有类的成员函数才能说明为虚函数,因为虚函数仅适合用与有继承关系的类对象,所以普通函数不能说明为虚函数。

3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个对象。

4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。

即使虚函数在类的内部定义定义,但是在编译的时候系统仍然将它看做是非内联的。

5.构造函数不能是虚函数,因为构造的时候,对象还是一片位定型的空间,只有构造完成后,对象才是具体类的实例。

6.析构函数可以是虚函数,而且通常声名为虚函数。

在编译时就能够确定哪个重载的成员函数被调用的情况被称做先期联编(earlybinding),而在系统能够在运行时,能够根据其类型确定调用哪个重载的成员函数的能力,称为多态性,或叫滞后联编(late binding)//例程3#include <iostream>usingnamespacestd;classVehicle{public:Vehicle(floatspeed,inttotal){Vehicle::speed=speed;Vehicle::total=total;}virtualvoidShowMember()//虚函数{cout<<speed<<"|"<<total<<endl;}protected:floatspeed;inttotal;};classCar:publicVehicle{public:Car(intaird,floatspeed,inttotal):Vehicle(speed,total){Car::aird=aird;}virtualvoidShowMember()//虚函数,在派生类中,由于继承的关系,这里的virtual也可以不加{cout<<speed<<"|"<<total<<"|"<<aird<<endl; }public:intaird;};voidtest(Vehicle &temp){temp.ShowMember();}intmain(){Vehicle a(120,4);Car b(180,110,4);test(a);test(b);cin.get();}运行结果:120|4110|4|180//例程2#include <iostream>usingnamespacestd;classVehicle{public:Vehicle(floatspeed,inttotal){Vehicle::speed=speed;Vehicle::total=total;}voidShowMember(){cout<<speed<<"|"<<total<<endl;}protected:floatspeed;inttotal;};classCar:publicVehicle{public:Car(intaird,floatspeed,inttotal):Vehicle(speed,total)Car::aird=aird;}voidShowMember(){cout<<speed<<"|"<<total<<"|"<<aird<<endl; }protected:intaird;};voidtest(Vehicle &temp){temp.ShowMember();}voidmain(){Vehicle a(120,4);Car b(180,110,4);test(a);test(b);cin.get();运行结果:120|4110|4虚继承是为了在多继承的时候避免引发歧义,比如类A有个就是a,B继承了A,C也继承了A,当D多继承B,C时,就会有歧义产生了,所以要使用虚拟继承避免重复拷贝。

多态性与虚函数

多态性与虚函数

第6章多态性与虚函数6.1 多态性概述6.2 重载与再定义6.3 运算符重载6.4 虚函数6.5 抽象类6.1 多态性概述6.1.1 多态的类型 6.1.2 多态的实现6.1.1 多态的类型多态性是指同一个消息被不同类型的对象接收时产生不同的行为,特点就是一个接口,多个实现。

C++中多态性按照类型可以分为强制多态、过载多态、包含多态和参数多态四种。

z强制多态是指通过语义操作,强制数据做本不属于该类型数据的操作。

编译器内部的数据的隐式转换,比如3.0+4操作时转换成3.0+4.0就属于此种类型z重载多态是指函数重载,同名的操作在不同的环境下有不同的行为。

前面学习过的普通函数重载和将要学习的类成员函数重载、运算符重载都属于此种类型。

z包含多态是指在同一类族中定义于不同类中的同名函数的多态行为。

我们本章将要学习的虚函数就属于此种类型。

z参数多态是指功能、结构实现相同但所作用于的数据类型不同,也就是数据类型参数化的多态。

第七章中的函数模板和类模板就属于此种多态。

6.1.2 多态的实现多态是指同一个消息被不同的对象接收产生不同的行为,因此,多态在实现的时候必须确定消息的操作对象。

我们根据消息和对象相结合的时间分为两种:z在程序编译连接阶段完成的,也就是说在编译的过程中确定了消息的操作对象,我们称为静态绑定。

z在程序运行阶段完成的,也就是说在程序运行的过程中才确定消息的操作对象,我们称为动态绑定。

6.2 重载与再定义6.2.1 函数的重载 6.2.2 函数的再定义6.2.1 函数的重载函数重载是指功能相似,函数名相同但所带参数不同的一组函数。

这里的“所带参数不同”既可能是参数的数据类型不同也可能是参数的个数不同。

z普通函数的重载。

int abs(int val){return val<0? –val: val;}float abs(float val){return val>0? –val: val;}6.2.1 函数的重载z类成员函数的重载。

《软件工程》第5章 软件构造

《软件工程》第5章 软件构造

5.5 软件代码审查
为保证代码开发的质量,在编码完成后,进行 代码审查或称Review。
审查内容: 1. 程序的版式 2. 文件结构 3. 命名规则 4. 表达式与基本语句
5.6 软件复用
软件复用就是将已有的软件成分用于构造新的 软件系统,以达到提高软件系统的开发质量与效率, 降低开发成本的目的。
在面向对象语言中,类功能支持这种层次机制。 除了根结点外,每个类都有它的基类(base class)。 除 了 叶结 点 外 , 每 个类 都 有它 的 派生 类 (derived class)。一个派生类可以从它的基类那里继承所有 的数据和操作,并扩充自己的特殊数据和操作。
3. 多态性
所谓多态,是指一个名字(或符号)具有 多种含义。即相同的操作的消息发送给不同的 对象时,每个对象将根据自己所属类中定义的 操作去执行,产生不同的结果。
5
第五章
软件构造
软件的详细设计,完成了软件的过程性的描述, 接下来进入程序编码阶段。
软件构造(Software Construction)指通过 编码、验证、单元测试、集成测试和排错的组合, 创建一个可以工作的、有意义的软件。在本章中, 我们将聚焦在该领域当中的程序设计语言、设计、 编码和复用这几个话题,其余的将在后续章节中讨 论。
类型的能力选取 。 6. 软件开发人员的知识水平以及心理因素。要特别注
意选择语言时,尽量避免受外界的影响,盲目追求 高、新的语言。
5.2 程序设计方法
5.2.1 结构化程序设计 结构程序设计的概念最早是由E.W.Dijkstra提出来的。
结构化程序设计方法 (Structured Program), 即SP法是结构化开发方法的重要组成部分。
5.3.1 源程序文件 符号的命名。尽量用与实际意义相同或接近的标识 符命名。 源程序中的注释 注释可分为序言性注释和解释性注释。错误的注释 宁可不要。 源程序的书写格式

C++继承,虚函数与多态性专题

C++继承,虚函数与多态性专题

本文作者:黄邦勇帅学习本文首先你应熟悉C++中的构造函数,基本的类的声明及怎样初始化类,关于这些问题,请参看本人所作的《C++构造函数,复制构造函数和析构函数》一文,在这篇文章中作了详细的介绍。

本文分两部分即继承和虚函数与多态性,本文第一部分详细讲解了继承时的构造函数和析构函数的问题,父类与子类的同名变量和函数问题,最后介绍了多重继承与虚基类。

本文第二部分重点介绍了虚函数与多态性的问题,因此学习虚函数的基础是继承,因此在学习虚函数前应学好继承。

本文详细易懂,内容全面,是学习C++的不错的资料。

本文内容完全属于个人见解与参考文现的作者无关,其中难免有误解之处,望指出更正。

声明:禁止抄袭本文,若需要转载本文请注明转载的网址,或者注明转载自“黄邦勇帅”。

主要参考文献:1、C++.Primer.Plus.第五版.中文版[美]Stephen Prata著孙建春韦强译人民邮电出版社2005年5月2、C++.Primer.Plus.第四版.中文版Stanley B.Lippman、Barbara E.Moo著李师贤等译人民邮电出版社2006年3月3、C++.Primer.Plus.第三版.中文版Stanley B.Lippman等著潘爱民张丽译中国电力出版社2002年5月4、C++入门经典第三版[美]Ivor Horton著李予敏译清华大学出版社2006年1月5、C++参考大全第四版[美]Herbert Schidt著周志荣朱德芳于秀山等译电子工业出版社2003年9月6、21天学通第四版C++ [美]Jesse Liberty著康博创作室译人民邮电出版社2002年3月14 继承(基类,父类,超类),派生类,子类一:继承中的访问权限关系。

1.基类,父类,超类是指被继承的类,派生类,子类是指继承于基类的类.2.在C++中使用:冒号表示继承,如class A:public B;表示派生类A从基类B继承而来3.派生类包含基类的所有成员,而且还包括自已特有的成员,派生类和派生类对象访问基类中的成员就像访问自已的成员一样,可以直接使用,不需加任何操作符,但派生类仍然无法访问基类中的私有成员.4.在C++中派生类可以同时从多个基类继承,Java不充许这种多重继承,当继承多个基类时,使用逗号将基类隔开.5.基类访问控制符,class A:public B基类以公有方式被继承,A:private B基类以私有方式被继承,A:protected B基类以受保护方式被继承,如果没有访问控制符则默认为私有继承。

虚函数的多态性机制分析

虚函数的多态性机制分析

R c nl (o be my, e t ge d u l l a
例 1以 下程 序演 示静 态 联编 . 该程 d u l my {= lW= y } o be w)L my; m w; d u l ae ( o s { tr o be ra )c nt r un e
< 函数 体> 其 中 .iu l 关键 字 , 定 义该 函 v ta 是 r 它
数 是 虚 函 数
Fg r( { ; i e)} u
du l ae ( o s rtr o be ra )c nt{ un e } ;
ca s C r l l s ic e : p b i F g r ul c i e u

2 虚 函数
有 了虚 函数 . 使 得动 态 联 编成 为 就
可能 . 可 以在 基类 和派 生类 之 间 实现 就
动 态 多 态 性
/ 程序 例 子. p / c p / 静态联 编 的问题 /
椭n ld <o te m> eu e isra c n td u l I .4; o s o be P =31
u ig n mep c t sn a s a e sd;
在 程 序 代 码 中 使 用 vr a 来 指 明 i ul t
某个 成员 函数 是 虚 函数 虚 函数 的定义 格式 :
cas Fg r ls i e u
/ 定 义 基 类 /

p bi ul c:
vr a ( 回 值 类 型 ) 函 数 名 ) i ul 返 t ( 00 } .;
如 果 编 程 者 把 某 类 中 的一 个 或 多
那 译 连 接 阶段 会 根 据 源程 序 中 的 要 调 用 个 成 员 函数 定义 为 虚 函数 . 么该 成 员

虚函数的概念与作用

虚函数的概念与作用

虚函数的概念与作用虚函数是面向对象编程中的一个重要概念,它可以使得在派生类中覆盖基类中的同名函数,实现多态性。

在C++中,虚函数的概念通过在成员函数前面加上关键字“virtual”来实现。

虚函数的作用主要包括实现运行时多态性、实现接口和抽象类以及消除静态绑定带来的限制等。

首先,虚函数的作用之一是实现运行时多态性。

多态性是面向对象编程中的一个重要特性,它指的是同样的一个函数能够根据不同的情况执行不同的功能。

通过使用虚函数,可以在基类和派生类之间建立动态绑定关系,使得运行时可以根据对象的实际类型来调用不同的函数实现。

这样,在多态的情况下,同一个函数调用可能会根据不同的对象类型而执行不同的操作,提高了代码的灵活性和可扩展性。

其次,虚函数的作用之二是实现接口和抽象类。

在面向对象编程中,接口是描述对象行为的抽象类。

通过将函数定义为虚函数,可以使得这些函数成为接口的一部分。

派生类可以继承基类的接口,并实现这些虚函数来满足具体的需求。

接口的作用是将对象的行为与具体的实现分离,提供了一种规范化的方式来描述对象的行为。

此外,虚函数的作用之三是消除静态绑定带来的限制。

对于静态绑定,函数调用的目标在编译时就能确定,因此对于基类指针或引用指向派生类对象时,只会调用基类中的对应函数。

而使用虚函数,则能够实现动态绑定,即在运行时根据对象的实际类型来确定函数调用的目标。

通过使用虚函数,可以使得基类指针或引用调用派生类中的函数而非基类中的函数,从而消除了静态绑定带来的限制。

虚函数的实现机制是通过在对象的内存模型中添加一个虚函数表(vtable)来实现的。

每个包含虚函数的类都有一个对应的虚函数表,其中记录了虚函数的地址。

派生类会继承基类的虚函数表,并在需要的情况下进行覆盖或添加新的虚函数,从而实现多态性。

在运行时,通过对象的虚函数指针(vptr)来访问虚函数表,并根据对象的实际类型来进行函数调用。

总之,虚函数是面向对象编程中非常重要的一个概念,它通过实现运行时多态性、实现接口和抽象类以及消除静态绑定带来的限制等方式,提高了代码的灵活性和可扩展性。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

IC-MSP V1.0
示例
例:复数类加减法运算重载-友元函数形式 #include <iostream.h> class complex { public: complex(double r=0.0,double i=0.0) {real=r;imag=i;}
friend complex operator +(complex c1,complex c2); friend complex operator –(complex c1,complex c2); IC-MSP V1.0
A& A::operator = (A &p) { X = p.X; Y = p.Y; cout << "Assigment operator called.\n"; return *this; } void main( ) { A a(7, 8); A b; b = a; cout << b.getX( ) << "," << b.getY( ) << endl; } IC-MSP V1.0
IC-MSP V1.0
函数重载方式2
2. 基类成员函数在派生类中重载 在种形式区分: (1)使用“类名::”加以区分 (2) 根据调用函数对象加以区分。
IC-MSP V1.0
示例
#include <iostream.h> class point { int x,y; public: point(int x,int y) { point::x=x; point::y=y; } float area() {return 0.0;} }; class circle:public point { int r; public: circle(int x,int y,int r1):point(x,y) { r=r1;} float area(){ return 3.14*r*r;} }; void main() { point p1(10,10); circle c1(5,5,10); cout << p1.area() << "\n"; //根据调用函数对象加以区分 cout << c1.area() << endl; cout << c1.point::area() << endl;//使用“类名::”加以区分 }
两种重载形式的比较
单目运算符最好被重载为成员函数;对双目运算符最好 被重载为友元函数 考虑一个表达式: 5.67 + c 重载为友元函数时,该表达式将被解释为: operator+(complex (5.67), c) V 重载为成员函数时,该表达式将被解释为: 5.67.oprator+(c) X 有的双目运算符重载为成员函数为好,例如,赋值运算 符 IC-MSP V1.0
void main() {complex c1(5,4),c2(2,10),c3; cout<<“c1=”;c1.display(); cout<<“c2=”;c2.display(); c3=c1-c2; cout<<“c3=c1-c2=”; c3.display(); c3=c1+c2; cout<<“c3=c1+c2=”; c3.display(); } IC-MSP V1.0
IC-MSP V1.0
重载为友元函数
重载为友元函数的定义格式如下: friend <类型说明符> operator <运算符> (< 参数表 >) {……} 表达式 c1 + c2 被编译程序解释为: operator+(c1,c2) 当重载友元函数时,将没有隐含的参数 this 指针 对双目运算符,友元函数有两个参数;对单目运算符,友 元函数有一个参数 不能重载为友元函数的运算符是:=,(),[ ]和 ->
函数重载方式1
1. 在一个类中说明的重载
在一个类中说明的重载函数之间靠所带参数个数或参数类型的不同加以区分
class point { int x,y; public: point(){x=0;y=0;} point(int x,int y) { point::x=x;point::y=y;} point(int i) { x=y=i;} }; void main() { point p1; point p2(2,3); point p3(6); }


实例(续)
#include <iostream.h> class complex { public: complex(double r=0.0,double i=0.0) {real=r;imag=i;} complex operator +(complex c2); complex operator -(complex c2); void display(); private: double real; double imag; }; IC-MSP V1.0
IC-MSP V1.0
函数重载应注意问题


不要使用重载函数来描述毫无相干的函数 构造函数可以重载,普通成员函数也可以重载
如果两个函数的参数表相同,但是返回类型不同,则第一个声明被视为第一 个的错误重复声明,会被标记为编译错误
• •

unsigned int Max( int i1, int i2 ); int Max( int , int ); // 错误:只有返回类型不同 在重载函数中使用缺省函数参数时应注意调用的二义性,例如: void print(int a, int b) { cout << "a=" << a << ",b=" << b << endl; } void print(int a, int b, int c = 50) { cout << "a=" << a << ",b=" << b << endl; } 下列函数调用,由于二义性将通不过: print(10,100);
面向对象的多态:
在面向对象中,多态性是指发出同样的消息被不同类型 的对象接收时导致完全不同的行为
IC-MSP V1.0
多态的种类
1. 编译时多态 是指在程序编译阶段即可确定下来的多态性,主要通 过重载机制获得。包括函数重载和运算符重载。 2. 运行时多态 是指必须等到程序动态运行时才可确定的多态,主要 通过继承结合虚函数来完成的。普通函数使用静态绑定, 而虚函数使用动态绑定。
};
IC-MSP V1.0
那么可以这样定义两个复数类的对象: complex a(10,20),b(5,8); 那么用a+b是否可以得到这两个复数的和呢?
IC-MSP V1.0
运算符重载概述
运算符重载的规则如下:
几乎所有的运算符都可以用作重载,下列运算符不允许重 载:.,*,:: ,?:,# 重载之后运算符的优先级和结合性都不会改变。 运算符重载是针对新类型数据的实际需要,对原有运算符 进行适当的改造。一般来讲,重载的功能应当与原有功能 相类似,不能改变原运算符的操作对象个数,同时至少要 有一个操作对象是自定义类型。
手机移动设备嵌入式C++
第五章 多态性和虚函数
IC-MSP V1.0
回顾
基类和派生类 单继承 多继承 虚基类
IC-MSP V1.0
目标
多态的概念 函数重载 运算符重载
了解静态联编和动态联编
虚函数
理解抽象类的概念和用法
IC-MSP V1.0
多态性的基本概念
程序的多态性
比如:C语言中的运算符”/”对于不同的数据类型具有 不同的含义。 6/4=1 6.0/4=1.5 6/4.0=1.5 6.0/4.0=1.5 程序中同一符号或名字在不同情况下具有不同解释的现 象称为多态性。
运算符重载
1. 重载为类的成员函数
重载函数为类的成员函数的格式如下: <类名> operator <运算符>(<参数表>) 表达式 c1 + c2 被编译程序解释为: c1.operator+(c2) 重载函数为类成员函数时,总隐含了一个参数,该参数 为this指针 对双目运算符,有一个参数;对单目运算符,不再显式 说明参数 IC-MSP V1.0
void display(); private: double real;double imag; }; void complex::display() {cout<<“(“<<real<<“,”<<imag”)”<<endl;} complex operator +(complex c1,complex c2) {return complex(c2.real+c1.real,c2.imag+c1.imag);} complex operator –(complex c1,complex c2) {return complex(c1.real-c2.real,c1.imag-c2.imag);} IC-MSP V1.0
IC-MSP V1.0
函数重载
函数重载 C++语言中,只要在声明函数原型时形式参数 的个数或者对应位置的类型不同,两个或更多的 函数就可以共用同一个名字。这种在同一作用域 中允许多个函数使用同一函数名的措施称为重载。 函数重载的方法
int abs(int x){…} double abs(double x){…} long abs(long x){…} main(){ abs(-5); abs(-5L); abs(3.14); } IC-MSP V1.0
相关文档
最新文档