11多态性与虚函数
- 格式:doc
- 大小:110.50 KB
- 文档页数:12
一、多态性(多态性的基本概念、虚函数和抽象类)单选题20道:1、下列关于虚函数的描述中,正确的是()。
A:虚函数是一个static类型的成员函数B:虚函数既可以是成员函数,以可以是非成员函数C:基类中说明一个虚函数后,派生类中定义相同原型的函数可以不必加virtual说明D:派生类中的虚函数与基类中相同原型的虚函数具有不同的参数个数或类型2、实现多态性的技术是()。
A:抽象类B:前期绑定C:友元函数D:动态绑定3、下列关于虚函数的说明中,正确的是()。
A:从虚基类继承的函数都是虚函数B:虚函数不得是静态成员函数C:只能通过指针或引用调用虚函数D:抽象类中的成员函数都是虚函数4、在派生类中重新定义虚函数时,除了(),其他方面都应与基类中相应的虚函数保持一致。
A:函数名B:参数C:函数体D:返回值类型5、关于纯虚函数和抽象类的描述中,错误的是()。
A:纯虚函数是一种特殊的虚函数,它没有具体的实现B:抽象类是指具有纯虚函数的类C:一个基类中说明有纯虚函数,该类的派生类一定不再是抽象类D:抽象类作为基类来使用,其纯虚函数的实现由派生类给出6、下面关于虚函数的描述,错误的是()。
A:在成员函数声明的前面加上virtual修饰,就可把该函数声明为虚函数B:基类中说明了虚函数后,派生类中对应的函数也必须说明为虚函数C:虚函数可以是另一个类的友元函数,但不能是静态成员函数D:基类中说明的纯虚函数在其任何派生类中都必须实现7、虚函数必须是类的( )。
A:友元函数B:成员函数C:构造函数D:析构函数8、多态性提高了系统的()。
A:一致性B:灵活性C:可维护性D:包括以上三项9、无论是虚函数还是实函数,在派生类被重定义后,原来的函数版本会()。
A:自动删除B:被新函数覆盖C:被隐藏D:无法操作10、抽象类的主要作用是()。
A:直接用作基类B:直接用作指针C:引用的基类型D:包括以上各项11、如果一个类至少有一个纯虚函数,那么就称该类是()。
一.实验目的及要求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;}五.实验总结通过本次试验 我更深刻的理解了某些语句如何使用及结构体的优点 也能更加熟练的编写简单的程序了 我深知实践要比书本更加重要 今后还要多练习 在实践中学习。
多态性与虚函数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)是抽象类的特性。
多态性与虚函数实验报告实验目的:通过实验掌握多态性和虚函数的概念及使用方法,理解多态性实现原理和虚函数的应用场景。
实验原理:1.多态性:多态性是指在面向对象编程中,同一种行为或者方法可以具有多种不同形态的能力。
它是面向对象编程的核心特性之一,能够提供更加灵活和可扩展的代码结构。
多态性主要通过继承和接口来实现。
继承是指子类可以重写父类的方法,实现自己的特定行为;接口是一种约束,定义了类应该实现的方法和属性。
2.虚函数:虚函数是在基类中声明的函数,它可以在派生类中被重新定义,以实现多态性。
在类的成员函数前面加上virtual关键字,就可以将它定义为虚函数。
当使用基类指针或引用调用虚函数时,实际调用的是派生类的重写函数。
实验步骤:1. 创建一个基类Shape,包含两个成员变量color和area,并声明一个虚函数printArea(用于打印面积。
2. 创建三个派生类Circle、Rectangle和Triangle,分别继承Shape类,并重写printArea(函数。
3. 在主函数中,通过基类指针分别指向派生类的对象,并调用printArea(函数,观察多态性的效果。
实验结果与分析:在实验中,通过创建Shape类和派生类Circle、Rectangle和Triangle,可以实现对不同形状图形面积的计算和打印。
当使用基类指针调用printArea(函数时,实际调用的是派生类的重写函数,而不是基类的函数。
这就是多态性的实现,通过基类指针或引用,能够调用不同对象的同名函数,实现了对不同对象的统一操作。
通过实验1.提高代码的可扩展性和灵活性:通过多态性,可以将一类具有相似功能的对象统一管理,节省了代码的重复编写和修改成本,增强了代码的可扩展性和灵活性。
2.简化代码结构:通过虚函数,可以将各个派生类的不同行为统一命名为同一个函数,简化了代码结构,提高了代码的可读性和维护性。
3.支持动态绑定:通过运行时的动态绑定,可以根据对象的实际类型来确定调用的函数,实现了动态绑定和多态性。
第10章多态性与虚函数一、单项选择题1.实现运行时的多态性要使用(D)。
A.重载函数B.构造函数C.析构函数D.虚函数2.通过运算符重载,可以改变运算符原有的(A)。
A.操作数类型B.操作数个数C.优先级D.结合性3.将运算符重载为类成员函数时,其参数表中没有参数,说明该运算符是(B)。
A.不合法的运算符B.一元运算符C.无操作数的运算符D.二元运算符4.在重载一个运算符时,其参数表中没有任何参数,说明该运算符是(B)。
A.作为友元函数重载的一元运算符B.作为成员函数重载的一元运算符C.作为友元函数重载的二元运算符D.作为成员函数重载的二元运算符5.如果表达式++a中的"++"是作为成员函数重载的运算符,若采用运算符函数调用格式,则可表示为(D)。
A.a.operator++(1)B.operator++(a)C.operator++(a,1)D.a.operator++()6.如果表达式a>=b中的">="是作为非成员函数重载的运算符,则可以等效地表示为(C)。
A.a.operator>=(b)B.b.operator>=(a)C.operator>=(a,b)D.perator>=(b,a)7.有如下程序:#include<iostream>using namespace std;class A{public:virtual void funl (){cout<<"A1";}void fun2 (){cout<<"A2";}};class B: public A{public:void funl (){cout<<"Bl";}void fun2 (){cout<<"B2";}};int main(){A*p=new B;p->funl ();p->fun2();return 0;}程序执行后,输出结果为(C)。
第10章多态性与虚函数【内容提要】多态性的概念;函数和运算符的重载;虚函数和抽象类。
【重点与难点】10.1 多态性的概念在面向对象的概念中,多态性是指不同对象接收到相同消息时,根据对象类的不同产生不同的动作。
由静态联编支持的多态性称为编译时的多态性或静态多态性,也就是说,确定同名操作的具体操作对象的过程是在编译过程中完成的。
C++用函数重载和运算符重载来实现编译时的多态性。
由动态联编支持的多态性称为运行时的多态性活动太多态性,也就是说,确定同名操作的具体操作对象的过程是在运行过程中完成的。
C++用继承和虚函数来实现运行时的多态性。
10.2 函数和运算符的重载10.2.1 函数重载面向对象程序设计中,函数的重载表现为两种情况:第一种是参数个数或类型有所差别的重载,第二种是函数的参数完全相同但属于不同的类。
10.2.2 运算符重载C++预定义的运算符只是对基本数据类型进行操作,而对于自定义的数据类型比如类,却没有类似的操作。
为了实现对自定义类型的操作,就必须自己编写程序来说明某个运算符作用在这些数据类型上时,应该完成怎样的操作,这就要引入运算符重载的概念。
运算符的重载形式有两种,一种是重载为类的成员函数,一种是重载为类的友元函数。
成员运算符函数的定义:在类内声明的一般形式为:<返回类型> operator<运算符>(参数表);在类外定义的一般形式为:<返回类型> <类名∷> operator<运算符>(参数表){函数体}其中,operator是定义运算符重载函数的关键字;运算符是要重载的运算符的名称;参数表给出重载运算符所需要的参数和类型。
将重载的运算符函数定义为类的友元函数,称为友元运算符函数。
友元运算符函数不友员运算符函数的定义:在类内声明的一般形式为:friend<返回类型> operator<运算符>(参数表);在类外定义的一般形式为:<返回类型> operator<运算符>(参数表){函数体}其中,friend是声明友元函数的关键字,operator是定义运算符重载函数的关键字;运算符是要重载的运算符的名称;参数表给出重载运算符所需要的参数和类型。
几种典型运算符的重载①加法运算符“+”的重载②“++”和“--”的重载③赋值运算符“=”的重载④函数调用运算符“()”的重载⑤下标运算符“[ ]”的重载10.3 虚函数和抽象类虚函数是重载的另一种形式,实现的是动态的重载,即函数调用与函数体之间的联系是在运行时才建立,也就是动态联编。
10.3.1 虚函数的定义和使用虚函数的定义是在基类中进行的,即把基类中需要定义为虚函数的成员函数声明为virtual。
当基类中的某个成员函数被声明为虚函数后,它就可以在派生类中被重新定义。
在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数和类型、参数的顺序都必须与基类中的原型完全一致。
虚函数定义的一般形式为:virtual<函数类型><函数名>(参数表){函数体}使用虚函数时应注意如下问题:①虚函数的声明只能出现在类声明的函数原型的声明中,不能出现在函数体实现的时候,而且,基类中只有保护成员或公有成员才能被声明为虚函数。
②在派生类中重新定义虚函数时,关键字virtual可以写也可不写,但在容易引起混乱时,应写上该关键字。
③动态联编只能通过成员函数来调用或通过指针、引用来访问虚函数,如果用对象名的形式来访问虚函数,将采用静态联编。
④虚函数必须是所在类的成员函数,不能是友元函数或静态成员函数。
但可以在另一个类中被声明为友元函数。
⑤构造函数不能声明为虚函数,析构函数可以声明为虚函数。
⑥由于内联函数不能在运行中动态确定其外治,所以它不能声明为虚函数。
10.3.2 纯虚函数和抽象类抽象类是一种特殊的类,它为一族类提供统一的操作界面,建立抽象类就是为了通过它多态地使用其中的成员函数。
抽象类是带有纯虚函数的类。
一个抽象类至少带有一个纯虚函数。
纯虚函数是在一个基类中说明的虚函数,它在该基类中没有具体的操作内容,要求各派生类在重新定义时根据自己的需要定义实际的操作内容。
纯虚函数的一般定义形式为:virtual<函数类型><函数名>(参数表)=0;纯虚函数与普通虚函数的定义的不同在于书写形式上加了“=0”,说明在基类中不用定义该函数的函数体,它的函数体由派生类定义。
如果一个类中至少有一个纯虚函数,这个类就成为抽象类。
它的主要作用是为一个族类提供统一的公共接口,以有效地发挥多态的特性。
使用时应注意以下问题:①抽象类只能用作其它类的基类,不能建立抽象类的对象。
因为它的纯虚函数没有定义功能。
②抽象类不能用作参数类型、函数的返回类型或显式转换的类型。
③可以声明抽象类的指针和引用,通过它们,可以指向并访问派生类对象,从而访问派生类的成员。
④若抽象类的派生类中没有给出所有纯虚函数的函数体,这个派生类仍是一个抽象类。
若抽象类的派生类中给出了所有纯虚函数的函数体,这个派生类不再是一个抽象类,可以声明自己的对象。
【典型例题】例题1.下面关于虚函数和函数重载的叙述不正确的是()。
(a)虚函数不是类的成员函数(b)虚函数实现了C++的多态性(c)函数重载允许非成员函数,而虚函数则不行(d)函数重载的调用根据参数的个数、序列来确定,而虚函数依据对象确定解答:函数重载和虚函数是C++中实现多态性的两种手段,但是它们的实现机制是不一样的;函数重载依据调用时的参数进行区分,而虚函数则根据对象实际的指向确定调用的版本。
答案为:a。
例题2.()是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。
(a)纯虚函数(b)虚析构函数(c)虚构造函数(d)静态成员函数解答:抽象类中的纯虚函数没有具体的定义,需要在抽象类的派生类中定义。
因此,纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。
答案为:a例题3.实现运行时的多态性要使用()。
(a)构造函数(b)析构函数(c)重载函数(d)虚函数解答:动态联编要在程序运行时才能确定调用哪个函数。
虚函数是实现动态联编的必要条件之一,没有虚函数一定不能实现动态联编。
答案为:d。
例题4.关于虚函数的描述中,()是正确的。
(a)派生类的虚函数与基类的虚函数具有不同的参数个数和类型(b)基类中说明勒虚函数后,派生类中其对应的函数一定要说明为虚函数(c)虚函数是一个成员函数(d)虚函数是一个static类型的成员函数解答:为实现某种功能而假设的函数称为虚函数。
虚函数是用关键字virtual进行说明。
虚函数是动态联编的基础。
虚函数只能是类中的一个成员函数,但不能是静态成员;派生类的虚函数与基类的虚函数具有相同的参数个数和类型,当派生类的虚函数与基类中的对应的虚函数的参数不同时,派生类的虚函数将丢失虚特性,变为重载函数;对于基类中的虚函数,在派生类中自然是虚函数,可不必说明。
答案为:c。
例题5.下面的程序中,有错误的语句是__________。
class A //①{public: //②A(){func(); //③}virtual void func()=0; //④};解答:在成员函数内可以调用纯虚函数,但在构造函数或析构函数内调用一个纯虚函数将导致程序运行错误,因为没有为纯虚函数定义代码。
答案为:③例题6.运行下列程序的结果为__________________。
#include<iostream.h>class basepublic:void display1(){cout<<"base::display1()"<<endl;}virtual void display2(){cout<<"base::display2()"<<endl;}};class derived:public base{public:void display1(){cout<<"derived::display1()"<<endl;}void display2(){cout<<"derived::display2()"<<endl;}};void main(){base * pbase;derived d;pbase=&d;pbase->display1 ();pbase->display2();}解答:本题主要考查有关多态性的相关知识。
在基类base中,定义了一个函数display1()和虚函数display2();在派生类derived中,重写了函数display1(),而且重新定义的虚函数display2()。
由于基类指针pbase指向的是派生类的一个对象,因而会调用派生类的display2()版本,但是对于一般的成员函数display1(),仍然遵循一般的调用规则,只调用基类的display1()版本。
本题答案为:base::display1()derived::display2()例题7.下面的程序的输出结果为an animal a person an animal a person,请将程序补充完整。
#include<iostream.h>class animal{public:______①_____ void speak(){cout<<"An animal"<<" ";}};class person:public animal{public:void speak(){cout<<"a person"<<" ";}};void main(){animal a,_______②_______;person p;a.speak();p.speak();pa=&a;pa->speak();________③_______;pa->speak();}解答:本题主要考查对多态性的理解与应用。
本题通过虚函数实现多态性,所以在基类中应定义虚函数;为了实现多态性,必须定义基类的指针,然后将它指向各个派生类的对象。
本题答案为:①virtual、②*pa、③pa=&p【习题】一、选择题1.在C++中,用于实现运行时多态性的是()。
A)内联函数B)重载函数C)模板函数D)虚函数2.如果一个类至少有一个纯虚函数,那么就称该类为()。