2014210723李玉花 实验9多态与虚函数2014
- 格式:doc
- 大小:72.04 KB
- 文档页数:6
一.实验目的及要求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;}五.实验总结通过本次试验 我更深刻的理解了某些语句如何使用及结构体的优点 也能更加熟练的编写简单的程序了 我深知实践要比书本更加重要 今后还要多练习 在实践中学习。
虚函数和多态性一、多态性的含义多态性是指通过(基类)指针或(基类)引用调用函数,这种调用是动态解析的,即在编译期间确定调用哪个函数。
因为派生类对象包含一个完整的基类对象,换句话说,每个派生类对象也是一个基类对象。
因此,可以用基类指针来存储派生类对象的地址。
但是,反之就不行。
不能用派生类的指针来存储基类对象的地址,因为每个基类对象只表示派生类的一部分。
从上图的派生关系,可以定义一个类Box的指针,来存储每个派生类的地址(包括多重派生类的地址)。
例如:CerealPack breakfast;Box* pBox=&breakfast;Carton* pCarton=&breakfast;Contents* pContents=&breakfast;这三个语句都是正确的,因为类Box是类CerealPack的间接基类,类Carton和类Content 是它的直接基类。
二、指针的静态类型和动态类型对于一个基类指针或引用,定义时的类型称为它的“静态类型”,实际指向对象时的类型称为“动态类型”。
在任意时刻,指针pBox都可以包含任何以Box为基类的派生类对象的地址。
该指针在声明时的类型是“Box*”,因此它的静态类型是“Box*”。
同时,它还具有动态类型。
当pBox 指向Carton对象时,其动态类型是“Carton*”。
当pBox指向ToughPack对象时,其动态类型是“ToughPack*”。
在pBox指向Box对象时,其动态类型和静态类型相同,是“Box*”类型。
编译器根据基类指针的动态类型来调用合适的虚函数版本,这个就是多态性最重要的内容。
三、多态类的含义当一个类至少包含一个虚函数时,称这个类具有多态性,或说这个类是多态类。
获得多态性有两种方式:从基类继承虚函数和自定义一个虚函数。
通过这两种方式,都可以获得多态性。
四、静态绑定和动态绑定㈠静态绑定和动态绑定的定义静态绑定又叫静态解析、静态调用,是指在源程序中确定的调用关系。
多态性与虚函数多态性的概念多态性(polymorphism)是面向对象程序设计的一个重要特征。
利用多态性可以设计和实现一个易于扩展的系统。
有过非面向对彖语言开发经历的人,通常对这一章节的内容会觉得不习惯,因为很多人错误地认为,支持类的封装的语言就是支持血向对象的,其实不然,Visual BASIC 6.0是典型的非面向对象的开发语言,但是它的确是支持类,支持类并不能说明就是支持面向对象,能够解决多态问题的语言,才是真正支持面向对象的开发的语言,所以务必提醒有过其它非面向对象语言基础的读者注意!多态的意思是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。
其实,我们己经接触过多态性的现象,例如函数的重载、运算符重载都是多态现象。
只是那时没有用到多态性这一专业术语而已。
例如,使用运算符”+”使两个数值相加,就是发送一个消息,它要调用operator+函数。
实际上,整型、单精度型、双精度型的加法操作过程是互不相同的,是由不同内容的函数实现的。
显然,它们以不同的行为或方法來响应同一消息。
在面向对象方法中一般是这样表述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。
从系统实现的角度看,多态性分为两类:静态多态性和动态多态性。
以前学过的函数重载和运算符重载实现的多态性属于静态多态性,在程序编译时系统就能决定调用的是哪个函数,因此静态多态性乂称编译时的多态性。
静态多态性是通过函数的重载实现的(运算符重载实质上也是函数重载)。
动态多态性是在程序运行过稈中才动态地确泄操作所针对的对彖。
它又称运行时的多态性。
动态多态性是通过虎函数(virtual function)实现的。
本章中主要介绍动态多态性和虚函数。
要研究的问题是:当一个基类被继承为不同的派生类时,各派生类可以使用与基类成员相同的成员名,如果在运行时用同一个成员名调用类对象的成员,会调用哪个对象的成员?也就是说,通过继承而产生了相关的不同的派生类,与基类成员同名的成员在不同的派生类中有不同的含义。
多态性与虚函数实验报告实验目的:通过实验掌握多态性和虚函数的概念及使用方法,理解多态性实现原理和虚函数的应用场景。
实验原理:1.多态性:多态性是指在面向对象编程中,同一种行为或者方法可以具有多种不同形态的能力。
它是面向对象编程的核心特性之一,能够提供更加灵活和可扩展的代码结构。
多态性主要通过继承和接口来实现。
继承是指子类可以重写父类的方法,实现自己的特定行为;接口是一种约束,定义了类应该实现的方法和属性。
2.虚函数:虚函数是在基类中声明的函数,它可以在派生类中被重新定义,以实现多态性。
在类的成员函数前面加上virtual关键字,就可以将它定义为虚函数。
当使用基类指针或引用调用虚函数时,实际调用的是派生类的重写函数。
实验步骤:1. 创建一个基类Shape,包含两个成员变量color和area,并声明一个虚函数printArea(用于打印面积。
2. 创建三个派生类Circle、Rectangle和Triangle,分别继承Shape类,并重写printArea(函数。
3. 在主函数中,通过基类指针分别指向派生类的对象,并调用printArea(函数,观察多态性的效果。
实验结果与分析:在实验中,通过创建Shape类和派生类Circle、Rectangle和Triangle,可以实现对不同形状图形面积的计算和打印。
当使用基类指针调用printArea(函数时,实际调用的是派生类的重写函数,而不是基类的函数。
这就是多态性的实现,通过基类指针或引用,能够调用不同对象的同名函数,实现了对不同对象的统一操作。
通过实验1.提高代码的可扩展性和灵活性:通过多态性,可以将一类具有相似功能的对象统一管理,节省了代码的重复编写和修改成本,增强了代码的可扩展性和灵活性。
2.简化代码结构:通过虚函数,可以将各个派生类的不同行为统一命名为同一个函数,简化了代码结构,提高了代码的可读性和维护性。
3.支持动态绑定:通过运行时的动态绑定,可以根据对象的实际类型来确定调用的函数,实现了动态绑定和多态性。
虚函数的定义要遵循以下重要规则: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时,就会有歧义产生了,所以要使用虚拟继承避免重复拷贝。