多态性和虚函数
虚函数与多态性实验
一.实验目的及要求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;}五.实验总结通过本次试验 我更深刻的理解了某些语句如何使用及结构体的优点 也能更加熟练的编写简单的程序了 我深知实践要比书本更加重要 今后还要多练习 在实践中学习。
c++虚函数作用及底层原理
c++虚函数作用及底层原理C++是一种面向对象的编程语言,并支持多态性的实现。
其中,虚函数是C++中实现多态性的主要机制之一。
虚函数是一种特殊的成员函数,可以在派生类中重写,并通过基类指针或引用的间接方式调用派生类的实现。
虚函数的作用:1. 实现动态绑定:实现多态性的关键是在运行时确定函数的具体实现。
虚函数通过将函数调用的确定延迟到运行时,而不是在编译时确定,从而实现动态绑定。
2. 多态性:允许派生类重写基类的函数,并使用基类指针或引用调用派生类的实现。
这种多态性的实现可以增强代码的灵活性和可重用性。
3. 实现抽象类:虚函数也可以用于实现抽象类,即只声明函数接口而没有实现。
这样,派生类必须实现虚函数才能被实例化。
虚函数的底层原理:虚函数的实现需要两个关键组件:虚函数表(vtable)和虚函数指针(vptr)。
1. 虚函数表:每个包含虚函数的类都有一个虚函数表,其中包含了类中所有虚函数的地址。
虚函数表是一个静态数据结构,只有一个虚函数表,且在编译时生成。
2. 虚函数指针:每个包含虚函数的类的对象都有一个虚函数指针,指向其所属类的虚函数表。
虚函数指针是一个指向虚函数表的指针,指针的值在程序运行时动态确定。
当调用虚函数时,程序首先通过对象的虚函数指针找到其所属类的虚函数表,然后查找相应的虚函数地址,最终调用正确的虚函数实现。
这样,虚函数的实现在运行时动态确定,实现了动态绑定和多态性。
总之,虚函数是C++中实现多态性的主要机制之一,通过虚函数表和虚函数指针的使用,实现了动态绑定和多态性的实现。
虚函数的应用可以增强代码的灵活性和可重用性。
c++多态性与虚函数习题
作业题一、写出下列程序运行结果1.#include<iostream>using namespace std;class A {public:virtual void func( ) {cout<<”func in class A”<<endl;} };class B{public:virtual void func( ) {cout<<”func in class B”<<endl;} };class C:public A,public B{public:void func( ) {cout<<”func in class C”<<endl:}};int main( ){C c;A& pa=c;B& pb=c;C& pc=c;pa.func( );pb.func( );pc.func( );}2.#include<iostream>using namespace std;class A{public:virtual ~A( ){cout<<”A::~A( ) called “<<endl; }};class B:public A{char *buf;public:B(int i) { buf=new char[i]; }virtual ~B( ){delete []buf;cout<<”B::~B( ) called”<<endl;}};void fun(A *a) {delete a;}int main( ){ A *a=new B(10);fun(a);}二、程序设计题1有一个交通工具类vehicle,将它作为基类派生小车类car、卡车类truck和轮船类boat,定义这些类并定义一个虚函数用来显示各类信息。
5.2定义一个shape抽象类,派生出Rectangle类和Circle类,计算各派生类对象的面积Area( )。
C--程序设计--第10章-多态性及虚函数
使用重载函数注意:
不要使用重载函数描述不相干的函数 在类中,构造函数和普通成员函数均可以
重载 避免与函数的默认参数产生二义性
二、运算符重载
运算符重载(operate overloading)就是 赋予已有的运算符多重含义。
运算符重载实质是函数重载,运算符重载 的选择与函数重载类似,会根据运算符的 操作数的类型、个数和顺序来进行运算符 函数的选择。
#include<iostream.h> str#iinngc:l:usdter<isntgr(icnhga.rh>*s) v{}ossccsssc{s{{ittohtttolsstlsssls*drruarrrueptrepttepsi1trii3tc{pn=rin=rrn=pmn.<nn.<lprgncngncign=agp<*ggp<auitepgtepnte'irssrssbv\hwy:hwyghwnsit1ssitsla0=(:=(:=(tnr=ttnrit'scssscs:sc)rt1"rrt3scesss~ivci;thpt1hpsih1(.T23(.t::tttsnohn}ra,r.a,tza()gh(()grrrrttiatlrsilrsrer";eass;eiiiirdre[)ne[1i;[Ttt1ttnnnniglnl;gnl.nlhl)rlggggnep*e(e}(gesgeiei;2e(((gtrsnsnstnp(nsns)ncsi(lipg)gthg)ig(;(htn)en;t;tr;t;nti)a)artnthhih}ths<<ri{((;+n++<p<snd))}1g1s1aere*ige;]]i]nonszl{{;&;z;ddgd)&eercseelrl;s=teo1)m;a;/18etu)om/)0ut..;)构sr<""/;pn<造);//;s;/复}lp函构e<制n<数造ge构tn函hd造;l数};重} 载
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多态性
虚析构函数
析构函数的作用是对象撤销之后清理现场。 在派生类对象撤销时,一般先调用派生类的 析构函数。再调用基类的析构函数。
然而,当定义的是一个指向基类的指针变量, 使用new运算符建立临时对象时,如果基类 中有析构函数,则在使用delete析构时只会 调用基类的析构函数。
这就需要将基类中的析构函数声明为虚函数。
虚函数的声明与使用
声明虚函数的一般格式如下: virtual 函数原型;
⑴ 必须首先在基类中声明虚函数。 ⑵ 派生类中与基类虚函数原型完全相同的成员函 数,即使在说明时前面没有冠以关键字virtual也 自动成为虚函数。
声明虚函数
⑶ 只有非静态成员函数可以声明为虚函数。 ⑷ 不允许在派生类中定义与基类虚函数名字及参数 特征都相同,仅仅返回类型不同的成员函数。 编译时 出错。 ⑸ 系统把函数名相同但参数特征不同的函数视为不 同的函数。 ⑹ 通过声明虚函数来使用C++提供的多态性机制时, 派生类应该从它的基类公有派生。
构函数等内容。
本章内容
静态联编与动态联编 虚函数的声明与使用 纯虚函数和抽象类 虚析构函数
Hale Waihona Puke 静态联编与动态联编所谓联编(tinding),就是使一个计算机程序的不同部 分彼此关联的过程。
静态联编在编译阶段完成,因为所有联编过程都在程 序开始运行之前完成,因此静态联编也叫先前联编或早期 联编。
另一种情况编译程序在编译时并不确切知道应把发送 到对象的消息和实现消息的哪段具体代码联编在一起,而 是在运行时才能把函数调用与函数体联系在一起,则称为 动态联编。
动态联编的实现
C ++语言中的动态联编是通过使用虚函数表 (Virtual Function Table)来实现的,虚函数表也称 为v-表。
多态性与虚函数实验报告
多态性与虚函数实验报告实验目的:通过实验掌握多态性和虚函数的概念及使用方法,理解多态性实现原理和虚函数的应用场景。
实验原理:1.多态性:多态性是指在面向对象编程中,同一种行为或者方法可以具有多种不同形态的能力。
它是面向对象编程的核心特性之一,能够提供更加灵活和可扩展的代码结构。
多态性主要通过继承和接口来实现。
继承是指子类可以重写父类的方法,实现自己的特定行为;接口是一种约束,定义了类应该实现的方法和属性。
2.虚函数:虚函数是在基类中声明的函数,它可以在派生类中被重新定义,以实现多态性。
在类的成员函数前面加上virtual关键字,就可以将它定义为虚函数。
当使用基类指针或引用调用虚函数时,实际调用的是派生类的重写函数。
实验步骤:1. 创建一个基类Shape,包含两个成员变量color和area,并声明一个虚函数printArea(用于打印面积。
2. 创建三个派生类Circle、Rectangle和Triangle,分别继承Shape类,并重写printArea(函数。
3. 在主函数中,通过基类指针分别指向派生类的对象,并调用printArea(函数,观察多态性的效果。
实验结果与分析:在实验中,通过创建Shape类和派生类Circle、Rectangle和Triangle,可以实现对不同形状图形面积的计算和打印。
当使用基类指针调用printArea(函数时,实际调用的是派生类的重写函数,而不是基类的函数。
这就是多态性的实现,通过基类指针或引用,能够调用不同对象的同名函数,实现了对不同对象的统一操作。
通过实验1.提高代码的可扩展性和灵活性:通过多态性,可以将一类具有相似功能的对象统一管理,节省了代码的重复编写和修改成本,增强了代码的可扩展性和灵活性。
2.简化代码结构:通过虚函数,可以将各个派生类的不同行为统一命名为同一个函数,简化了代码结构,提高了代码的可读性和维护性。
3.支持动态绑定:通过运行时的动态绑定,可以根据对象的实际类型来确定调用的函数,实现了动态绑定和多态性。
第六章 多态性
多态性的分类: 多态性的分类: 通用多态:参数多态、包含多态 通用多态:参数多态、 专用多态: 专用多态:重载多态和强制多态 参数多态与类属函数和类属类相关联,本书第8章将要介 参数多态与类属函数和类属类相关联,本书第8 绍的函数模板和类模板就是这种多态。 绍的函数模板和类模板就是这种多态。由类模板实例化的各 个类都具有相同的操作,而操作对象的类型却各不相同。 个类都具有相同的操作,而操作对象的类型却各不相同。同 样地,由函数模板实例化的各个函数都具有相同的操作, 样地,由函数模板实例化的各个函数都具有相同的操作,而 这些函数的参数类型是各不相同的。 这些函数的参数类型是各不相同的。 包含多态是研究类族中定义于不同类中的同名成员函数的 多态行为,主要是通过虚函数来实现的。 多态行为,主要是通过虚函数来实现的。
虚析构函数
virtual ~类名(); 类名(); 如果一个类的析构函数是虚函数,那么, 如果一个类的析构函数是虚函数,那么,由它派生而来 的所有派生类的析构函数也是虚析构函数, 的所有派生类的析构函数也是虚析构函数,不管它是否使用 丁关键字virtual 进行说明。 丁关键字virtual 进行说明。 析构函数设置为虚函数之后,在使用指针引用时可以动 析构函数设置为虚函数之后, 态联编,实现运行时的多态, 态联编,实现运行时的多态,保证使用基类类型的指针能够 调用适当的析构函数针对不同的对象进行清理工作。 调用适当的析构函数针对不同的对象进行清理工作。 例6.4 6.5 6.6
虚函数与重载函数的关系
普通的函数重载时, 普通的函数重载时,其函数的参数或参数类型必须有所 不同,函数的返回类型也可以不同。但是, 不同,函数的返回类型也可以不同。但是,当重载一个虚函 数时,也就是说在派生类中重新定义虚函数时,要求函数名、 数时,也就是说在派生类中重新定义虚函数时,要求函数名、 返回类型、参数个数、 返回类型、参数个数、参数的类型和顺序与基类中的虚函数 原型完全相同。如果仅仅返回类型不同,其余均相同, 原型完全相同。如果仅仅返回类型不同,其余均相同,系统 会给出错误信息; 若仅仅函数名相同,而参数的个数、 会给出错误信息;·若仅仅函数名相同,而参数的个数、类 型或顺序不同,系统将它作为普通的函数重载, 型或顺序不同,系统将它作为普通的函数重载,这时将丢失 虚函数的特性。 虚函数的特性。 例6.7
【大学】C++面向对象程序设计 多态性与虚函数
调用不同的类(基类或派生类)的虚函数,从而完成不同的功能,这
又是一种多态性的体现。
.
蚌埠学院计算机系 4
C++面向对象程序设计
9.1.2 静态多态性和动态多态性
编译时多态通过静态联编实现,运行时多态通过动态联 编实现。
1 联编 在面向对象程序设计中,联编(binding)的含义是把
一个函数名与其实现的代码联系在一起,即主调函数代码 必须与被调函数代码连接起来。
.
蚌埠学院计算机系 12
C++面向对象程序设计
9.2 对虚函数的限制
9.2.1 声明虚函数的限制
一般情况下,可将类中具有共性的成员函数声明为虚函数,而个 性的函数往往为某一个类独有,声明为一般成员函数。将类的成员函 数声明为虚函数有利于编程,但下面的函数不能声明为虚函数:
⑴构造函数不能声明为虚函数。构造函数在对象创建时调用,完成对象 的初始化,此时对象正在创建中,基类指针无从指向。只有在构造过 程完成后,对象才存在,才能被基类指针指向。
9.1.1 多态性的实现方法
同一段代码,当用不同的对象去调用时,该代码具有不同的功能,这 称为多态性。C++提供的多态性分为静态多态性(编译时多态)和动 态多态性(运行时多态)。静态多态性是一种编译时的多态,是通过 重载和模板实现的。动态多态性是一种运行时的多态,其基础是数据 封装和继承机制,通过继承建立类层次,并通过在基类中定义虚函数 来实现多态性,即在基类和派生类中建立同名的函数,但是函数的功 能是不同的。
2 静态多态性
在没有类层次的场合,使用函数重载的方式实现静态多态性。 各个重载函数名称相同,但参数表应在参数个数、类型和次序 上有所不同。编译器根据参数表来识别各个重载函数。根据参 数表,系统在编译时就完成静态联编的过程。关于没有类层次 的函数重载实现多态的例子前面已经介绍,这里不再赘述。
试谈C++的多态性和虚函数
动。按照联编所进行的阶段不同 , 可分为静态联编
和动态联编。静态联编是指在程序编译阶段进行
的联编 , 这种联编过程是在程序运行之前完成 的,
又称为早期联编。动态联编是指联编要在程序运 行时动态地进行 , 实际上是在运行时虚函数的实
{ul : pbc i
s t t d ( t ,i ) t ci d i n Y a na n x t i
1 1编译 时的 多 态性 .
它和封装性 、 继承性构成 了面向对象程序设计的三 大特性 。多态性是指 当不 同对象接受到相 同的消
法” 。多态性的重要 目标之一是实现程序中的表示 独立性, 从而提高程序的可扩展性与可复用性。
1多态性
静态联编支持的多态性称为编译时的多态性 , 也称静态多态性 , 它是通过函数重载和运算符重载
调用的是哪一个函数。 函数重载是指在相同的作用域 内, 同一个 函数
息时产生不 同的行为 , 从而实现“ 一个接 口, 多种方 实现的, 编译器对源程序进行编译时就可以确定所
名对应着多个不 同的实现。函数重载要求 : 函数的
可以是形参的类型不同, 或者两者都不相同。函数
程序 1 :
维普资讯
第8 卷第 4期
20 O6年 1 2月
黄 冈 职 业 技 术 学 院 学 报
V1 o 08 . .N 4
De .O 6 e20
试谈 C++的多态性和虚 函数
范秋 生
( 冈职 业技 术学院 黄 湖 北 黄冈 480 ) 302
摘 要: 本文分析 了C++中的两种多态性 : 编译时的多态性和运行时的多态性 , 通过 函数重裁和虚
・
8 - 2
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部分 多态性与虚函数
例: 虚函数成员
#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" 来表示。
包含纯虚函数的类被称为抽象类,它只能作为基类使用,不能被实例化。
综上所述,虚函数是实现多态性的关键机制之一。
通过在父类中声明虚函数并在子类中重写,可以实现基于对象实际类型的动态绑定,提高程序的灵活性和可扩展性。
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基类以受保护方式被继承,如果没有访问控制符则默认为私有继承。
《C++程序设计》电子教案第9章 多态性和虚函数
返回本节
9.1.3 派生类指针
指向基类和派生类的指针是相关的。 例如: A * p ; // 指向类型 A 的对象的指针 A A_obj ; // 类型 A 的对象 B B_obj ; // 类型 B 的对象 p = & A_obj ; // p 指向类型 A 的对象 p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
((B_class *)p)->show_phone( ); // 用基类指针访问公有派生类的特定成员,必须进行类型转换 } 此程序的运行结果为: Zhang San Li Si
0731_12345678 0731_12345678
例9-9:写出下面的程序的执行结果。 #include <iostream.h> class Student { public: Student(int xx) { x=xx; } virtual float calcTuition( ); protected: int x; }; float Student::calcTuition() { return float(x*x);
main ( ) { A_class *p; //对象指针 A_class A_obj; //对象 B_class * bp; B_class B_obj; p=&A_obj; //P指针指向基类对象,调用基类成员函数 p->put_name("Zhang San"); p=& B_obj; //P指针指向派生类对象,调用继承自基类的成 员函数 p->put_name("Li Si"); A_obj.show_name( ); B_obj.show_name( ); bp=&B_obj; bp->put_phone("0731_12345678"); bp->show_phone( );
虚函数的概念与作用
虚函数的概念与作用虚函数是面向对象编程中的一个重要概念,它可以使得在派生类中覆盖基类中的同名函数,实现多态性。
在C++中,虚函数的概念通过在成员函数前面加上关键字“virtual”来实现。
虚函数的作用主要包括实现运行时多态性、实现接口和抽象类以及消除静态绑定带来的限制等。
首先,虚函数的作用之一是实现运行时多态性。
多态性是面向对象编程中的一个重要特性,它指的是同样的一个函数能够根据不同的情况执行不同的功能。
通过使用虚函数,可以在基类和派生类之间建立动态绑定关系,使得运行时可以根据对象的实际类型来调用不同的函数实现。
这样,在多态的情况下,同一个函数调用可能会根据不同的对象类型而执行不同的操作,提高了代码的灵活性和可扩展性。
其次,虚函数的作用之二是实现接口和抽象类。
在面向对象编程中,接口是描述对象行为的抽象类。
通过将函数定义为虚函数,可以使得这些函数成为接口的一部分。
派生类可以继承基类的接口,并实现这些虚函数来满足具体的需求。
接口的作用是将对象的行为与具体的实现分离,提供了一种规范化的方式来描述对象的行为。
此外,虚函数的作用之三是消除静态绑定带来的限制。
对于静态绑定,函数调用的目标在编译时就能确定,因此对于基类指针或引用指向派生类对象时,只会调用基类中的对应函数。
而使用虚函数,则能够实现动态绑定,即在运行时根据对象的实际类型来确定函数调用的目标。
通过使用虚函数,可以使得基类指针或引用调用派生类中的函数而非基类中的函数,从而消除了静态绑定带来的限制。
虚函数的实现机制是通过在对象的内存模型中添加一个虚函数表(vtable)来实现的。
每个包含虚函数的类都有一个对应的虚函数表,其中记录了虚函数的地址。
派生类会继承基类的虚函数表,并在需要的情况下进行覆盖或添加新的虚函数,从而实现多态性。
在运行时,通过对象的虚函数指针(vptr)来访问虚函数表,并根据对象的实际类型来进行函数调用。
总之,虚函数是面向对象编程中非常重要的一个概念,它通过实现运行时多态性、实现接口和抽象类以及消除静态绑定带来的限制等方式,提高了代码的灵活性和可扩展性。
《C++面向对象程序设计》 谭浩强 第六章
C++
先声明基类point类,并调试之; 再声明派生类circle类,调试之; 最后声明cylinder类,调试之。
6- 5
① 先声明基类point
#include <iostream.h> class point //声明类point {public: point (float x=0, float y=0 ); //有默认值的构造函数 void setPoint (float, float); //设置点坐标 float getX ( ) const { return x; } // 读x 值 float getY ( ) const { return y; } // 读y 值 friend ostream & operator << ( ostream &, const point &); protected: float x,y; }; // 下面定义 point类的成员函数 point :: point (float a, float b) // point的构造函数 { x = a; y = b; } void point :: setPoint (float a, float b) { x =a; y = b; } ostream &operator << (ostream &output, const point &p) { // 重载运算符<< output<<“[x=“<<p.x<<“, y=“<<p.y<<“]”<<endl; return output; }
6Hale Waihona Puke 7③ 最后声明cylinder类
C++程序设计基础第6章 虚函数与多态性
6.2.1 虚函数的定义
2. 虚函数的定义 • 虚函数的定义是在基类中进行的 。 • 虚函数的定义语法格式如下:
virtual<函数类型><函数名>(形参表) {
函数体 }
12
6.2.1 虚函数的定义
3. 定义虚函数时,要注意遵循以下规则: 1)只有成员函数才能声明为虚函数,因为虚
函数仅适用于有继承关系的类对象,所以 普通函数不能声明为虚函数。 2)虚函数的声明只能出现在类声明中的函数 原型声明中,而不能出现在成员的函数体 实现上。 3)类的静态成员函数不可以定义为虚函数, 因为静态成员函数不受限于某个对象。
}
7
void main()
{
MaxClass mc(34,67,143,89);
cout<<"计算前两个数中的最大数为:“
<<mc.max(34,67)<<endl;
cout<<"计算前三个数中的最大数为:“
<<mc.max(34,67,143)<<endl;
cout<<"计算四个数中的最大数为:“
运行结果: 张晓强,园林工人 李文卓,生命科学教师
23
6.2.3 虚函数的重载
• 2. 多继承中的虚函数
【例6.8】多继承中使用虚函数例题。
#include <iostream.h>
class base1
//定义基类base1
{
public: virtual void display()
//函数定义为虚函数
运行结果:
(1) : 动物(食草/食肉). (2) : 食草动物 : 羚羊 (3) : 食草动物 : 羚羊 (4) : 食肉动物 : 老虎 (5) : 食肉动物 : 老虎 (6) : 食草动物 : 羚羊 (7) : 食肉动物 : 老虎
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
多态性和虚函数Problem A: C++习题抽象基类Description编写一个程序,声明抽象基类Shape,由它派生出3个派生类:Circle(圆形)、Rectangle(矩形)、Triangle(三角形),用一个函数printArea分别输出以上三者的面积(结果保留两位小数),3个图形的数据在定义对象时给定。
Input圆的半径矩形的边长三角形的底与高Output圆的面积矩形的面积三角形的面积Sample Input12.64.5 8.44.5 8.4Sample Outputarea of circle = 498.76area of rectangle = 37.80area of triangle = 18.90#include <iostream>#include <iomanip>using namespace std;class Shape{public:virtual double area()const=0;};class Circle:public Shape{public:Circle(double r):radius(r) {}virtual double area() const{return 3.14159*radius*radius;};protected:double radius;};class Rectangle:public Shape{public:Rectangle(double w,double h):width(w),height(h) {}virtual double area() const{return width*height;}protected:double width,height;};class Triangle:public Shape{public:Triangle(double w,double h):width(w),height(h) {}virtual double area()const{return 0.5*width*height;}protected:double width,height;};void printArea(const Shape &s){cout<<s.area()<<endl;}int main(){float r,a,b,w,h;cout<<fixed<<setprecision(2);cin>>r;Circle circle(r);cout<<"area of circle = ";printArea(circle);cin>>a>>b;Rectangle rectangle(a,b);cout<<"area of rectangle = ";printArea(rectangle);cin>>w>>h;Triangle triangle(w,h);cout<<"area of triangle = ";printArea(triangle);return 0;}Problem B: C++习题虚函数-计算图形面积Description编写一个程序,定义抽象基类Shape,由它派生出5个派生类:Circle(圆形)、Square(正方形)、Rectangle(矩形)、Trapezoid(梯形)、Triangle(三角形)。
用虚函数分别计算几种图形面积,并求它们之和。
要求用基类指针数组,使它每一个元素指向一个派生类对象。
Input圆的半径正方形的边长矩形的边长梯形的上底和下底,高三角形的底与高Output所有图形面积之和(结果保留两位小数)Sample Input12.63.54.5 8.42.0 4.53.24.5 8.4Sample Outputtotal of all areas=574.11#include <iostream>#include <iomanip>using namespace std;class Shape{public:virtual double area()const=0;};class Circle:public Shape{public:Circle(double r):radius(r) {}virtual double area() const{return 3.14159*radius*radius;};protected:double radius;};class Square:public Shape{public:Square(double s):side(s) {}virtual double area() const{return side*side;}protected:double side;};class Rectangle:public Shape{public:Rectangle(double w,double h):width(w),height(h) {}virtual double area() const{return width*height;}protected:double width,height;};class Trapezoid:public Shape{public:Trapezoid(double t,double b,doubleh):top(t),bottom(b),height(h) {}virtual double area()const{return 0.5*(top+bottom)*height;}protected:double top,bottom,height;};class Triangle:public Shape{public:Triangle(double w,double h):width(w),height(h) {}virtual double area()const{return 0.5*width*height;}protected:double width,height;};int main(){float r,a,b,w1,w2,w,h;cout<<fixed<<setprecision(2);cin>>r;Circle circle(r);cin>>a;Square square(a);cin>>a>>b;Rectangle rectangle(a,b);cin>>w1>>w2>>h;Trapezoid trapezoid(w1,w1,h);cin>>w>>h;Triangle triangle(w,h);Shape *pt[5]= {&circle,&square,&rectangle,&trapezoid,&tria ngle};double areas=0.0;for(int i=0; i<5; i++)areas=areas+pt[i]->area();cout<<"total of all areas="<<areas<<endl;return 0;}Problem C: 抽象一个形状类Description阮宝同学期待着暑假来临,知道C++不好好复习麻烦不小。
没有多态性,那就不叫面向对象,老师不划重点也能猜到。
嘿嘿,自己做个经典题,怎么变题也不怕。
老湿,再难的题还有木有?Input输入四个数,前两个是矩形的长和宽,后两个是三角形的底边长和高。
Output分两行输出两个数,第一个是矩形的面积,第二个是三角形的面积。
Sample Input3.5 6.43.5 6.4Sample Output22.411.2#include <iostream>using namespace std;class Shape{public:virtual double area()const=0;};class Rectangle:public Shape{public:Rectangle(double w,double h):width(w),height(h) {}virtual double area() const{return width*height;}protected:double width,height;};class Triangle:public Shape{public:Triangle(double w,double h):width(w),height(h) {}virtual double area()const{return 0.5*width*height;}protected:double width,height;};int main(){double a,b;cin>>a>>b; //输入矩形的长和宽Rectangle r(a,b);//建立Rectangle类对象r, 矩形长a宽bShape *s1=&r;cout<<s1->area()<<endl;//输出矩形的面积double w,h;cin>>w>>h; //输入矩形的长和宽Triangle t(w,h); //建立Triangle类对象t,三角形底边长w高hShape &s2=t;cout<<s2.area();//输出三角形面积return 0;}Problem D: 虚函数练习:动物2 Description长期的物种进化使自然界出现了生活在陆地上的陆生动物和生活在水中的水生动物。
根据已有主函数编写动物类,陆生动物类和水生动物类。
Input动物的体长,体重,性别;水生动物的体长,体重,性别,游泳速度;陆生动物的体长,体重,性别,奔跑速度;Output动物的体长,体重,性别;水生动物的体长,体重,性别,游泳速度;陆生动物的体长,体重,性别,奔跑速度;Sample Input52 22 f62 32 m 12272 42 m 102Sample Outputheight:52weight:22sex:fheight:62weight:32sex:mswimming_speed:122height:72weight:42sex:mrunning_speed:102#include <iostream>using namespace std;class animal{public:animal(int h,int w,char s){height=h;weight=w;sex=s;}virtual void display(){cout<<"height:"<<height<<endl;cout<<"weight:"<<weight<<endl;cout<<"sex:"<<sex<<endl;}protected:int height;int weight;char sex;};class aqu_animal:public animal{public:aqu_animal(int h,int w,char s,int s_p):animal(h,w,s),swimming_speed(s_p){} virtual void display(){cout<<"height:"<<height<<endl;cout<<"weight:"<<weight<<endl;cout<<"sex:"<<sex<<endl;cout<<"swimming_speed:"<<swimming_speed< <endl;}protected:int swimming_speed;};class ter_animal:public animal{public:ter_animal(int h,int w,char s,int r_p):animal(h,w,s),running_speed(r_p){}virtual void display(){cout<<"height:"<<height<<endl;cout<<"weight:"<<weight<<endl;cout<<"sex:"<<sex<<endl;cout<<"running_speed:"<<running_speed<<en dl;}protected:int running_speed;};int main(){int a,b,s,r;char c;animal *p;cin>>a>>b>>c;animal pa(a,b,c);p=&pa;p->display();cin>>a>>b>>c>>s;aqu_animal pb(a,b,c,s);p=&pb;p->display();cin>>a>>b>>c>>r;ter_animal pc(a,b,c,r);p=&pc;p->display();return 0;}Problem E: 交通工具信息Description有一个交通工具类vehicle,将它为基类派生的有派生小车类car,卡车类truck和轮船类boat,定义这些类,并使其能够显示其各类交通工具的详细信息。