C++_多态与虚函数

什么是多态?

一个 “操作” 随着所 ”传递或捆绑的对象类型的不同 “ 能够做出 ”不同的反应“,其行为模式称为多态。

即,对这个操作,能接受不同类型的参数,而且在处理参数时,会根绝其类型做出不同的反应。

为什么要引入多态?

针对由继承得到一类对象,在处理这类对象时,能够以同一操作处理完所有对象。

遇到的问题:

#include<iostream>
using namespace std;

class Base
{
public:
	void Show()
	{
		cout<<"Base"<<endl;
	}
};

class DeriveOne : public Base
{
public:
	void Show()
	{
		cout<<"DeriveOne"<<endl;
	}
};


void AAA(Base base)
{
	base.Show();
}


int main()
{
	Base base;
	DeriveOne deriveOne;
	
	AAA(base);
	AAA(deriveOne);

	system("pause");
	return 1;
}
输出


说明:

在父类Base和派生类DeriveOne中都定义了Show操作,它们分别输出对应类的信息,Base的show操作显示Base的信息,DeriveOne的show操作显示DeriveOne的信息。在函数AAA中,由于参数是base类型,在函数AAA中使用时,无论形参是父类Base还是子类DeriveOne,在函数中,其仅仅表示父类或者子类中的父类的那部分信息。若想显示完全,就必须写两个函数,它们分别接受两种参数,如:

void show(Base base);
void show(DeriveOne Derive);
问题又来了,由于派生类可以转化为基类,因此在调用Show函数时,还需要判断来的类是基类还是派生类。此时,用的时候就很不方便了。

此时,我们想要的是,函数AAA的获得实参可以是继承类集合中的任意类对象,而且其函数体也能根据其实参的改变调用不同的函数,即自动分别实参。这就是我们要求的多态。

举例说明要实现的目标:

#include<iostream>
using namespace std;

class Base
{
public:
	virtual void Show()
	{
		cout<<"Base"<<endl;
	}
};

class DeriveOne : public Base
{
public:
	 void Show()
	{
		cout<<"DeriveOne"<<endl;
	}
};

class DeriveTwo : public Base
{
public:
	void Show()
	{
		cout<<"DeriveTwo"<<endl;
	}
};

//定义一个操作,对多个不同的继承类对象做统一操作
void AAA(Base& base)  
{
	base.Show();
}

int main()
{
	Base base;
	DeriveOne deriveOne;
	DeriveTwo deriveTwo;

	AAA(base);
	AAA(deriveOne);
	AAA(deriveTwo);
	system("pause");
	return 1;
}

说明,这里还是给出一个函数AAA,其调用AAA的实参是有三个,base、deriveOne、deriveTwo,函数AAA能够根据传进来的实参来自定义判断调用哪个类的Show函数。这是我们想要的结果,即函数AAA能够接受整个派生类集合,并能区分不同的类对象。

怎么实现多态?

虚函数 + 基类指针引用调用。

什么是虚函数?

简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。

虚函数的原理:

虚函数的作用是实现类的继承所体现的多态性,具体点是实现动态联编。

从程序的角度上来说,在定义了虚函数后,可以在派生类中对虚函数重新定义,以实现统一的接口,不同定义过程,在程序的运行阶段动态地选择合适的成员函数。

一旦标记基类的函数为虚函数,便有连锁反应,后面继承的类中一切同名成员函数都变成了虚函数,(即使后面的这些同名函数没有声明该函数为虚函数)。

C++实现运行时多态性的关键途径:在公有派生情况下,一个指向基类的指针可用来访问从基类继承的任何对象。

虚函数语法:

对函数的设置:普通函数的前面加上virtual

virtual 函数返回值类型 虚函数名(形参表)
{ 
	//函数体
}

虚函数的调用方式:只能通过指向基类的指针或基类对象的引用来调用虚函数

调用语法:

指向基类的指针变量名->虚函数名(实参表)
基类对象的引用名. 虚函数名(实参表)

注意事项一:正常情况下,如果不把函数声明为虚函数,指向基类的指针的访问情况如下:

1)基类指针指向基类对象:基类指针可以直接访问基类对象中的成员

2)基类指针指向派生类对象:基类指针只能访问派生类中的从基类中继承的成员,派生类有同名的函数或成员,也只能调用基类的成员。

如果定义成虚函数时:定义一个基类指针,把不同的派生类对象付给它,会调用对应派生类的函数,而非基类函数。

举例:

#include <iostream>
using namespace std;
class A
{
public:
	virtual void show()
	{
		cout<<"A"<<endl;
	}
};
class B:public A
{
public:
	void show()
	{
		cout<<"B"<<endl;
	}
};
class C:public A
{
public:
	void show()
	{
		cout<<"C"<<endl;
	}
};
void main()
{
	A*a;
	B b;
	C c;
	a=&b;
	a->show();
	a=&c;
	a->show();
	system("pause");
}

运行结果:B(换行)C(换行)--指向不同的派生类,调用不同的函数

如果不加基类A中的Virtual,则输出结果:A(换行)A(换行)--基类指针,调用派生类中继承的基类成分

注意事项二:如果不是使用基类指针或引用,而仅仅是类之间的复制,那么子类对象就完全转换为父类对象,就不会有多态了。

Student s; //基类
GraduateStudent gs; //派生类

void fn (Student a)//形参传值--而非(Student & a)
{
	a.Show();
};

fn(gs); //无多态,调用基类的show函数,而不是派生类的show函数。

原因,在参数传递的过程中,已经将实参的性质做了肯定的转变,而对于确定的对象,是没有选择操作可言的。

因此,仅仅对于对象的指针和引用的间接访问,才会发生多态现象

定义虚函数,实现动态联编需要三个条件:

1)必须把动态联编的行为定义为类的虚函数---定义虚函数

2)类之间存在子类型关系,一般表现为一个类从另一个类公有派生而来---类之间是公有继承

3)基类指针指向派生类的对象,然后使用基类指针调用虚函数

把那些函数设置为虚函数?

如果一个函数在子类和父类中都有,而且子类使用这函数时,仅仅使用自己就ok,那么可以把这个函数设置为虚函数。

虚函数是怎么实现动态捆绑的?

编译器在编译的时候,看到一个基类函数前面有关键字”virtual“,编译器就知道需要对该函数进行晚捆绑,即不能立即断定虚函数的确切位置,此时编译器将此函数调用转化为一个指针。该指针在该虚函数被调用的时候,根据实际对象来给指针赋值,即指向实际对象的成员函数。这样一来,若实际对象是基类,则调用的是基类成员函数。若实际对象是派生类,则调用的是子类的成员函数。

为了给这个指针赋值,必须在每个实际的对象中额外占有一个指针空间,以指向自己类中的虚函数表。

如:


注意,具有虚函数的类,其对象空间比没有虚函数的类多了一个指针空间,而且在使用时需要间接访问虚函数,即采用虚函数,会影响一些程序运行效率。

关于虚函数的详细说明,请见另一篇文章:

虚函数的

注意:

1、使用时,虚函数可以在基类中声明,提供界面。可以在给派生类中定义具体的实现方法,而得到多种方法。

2、多态的实现必须是公有派生。

原因:虚函数为了实现多态,虚函数建立在赋值兼容原则上,而赋值兼容原则成立的前提条件是派生类从基类那里公有派生。

3、派生类对基类中虚函数重新定义时,关键字可以写也可以不写,系统可以自动判断。

因为,虚函数会自动地从基类往下传播,可以虚到底。在函数调用时,总是先碰见基类的函数,只要把该函数设置为虚函数,系统就认为其他派生类函数就为虚的啦。

4、虚函数必须是类的成员函数,不能是友元函数,也不能是静态成员函数。

因为虚函数仅仅适用于有继承关系的类对象,所有普通函数不行。由于静态函数不收对象的捆绑,即使在形象上的捆绑,实际上也没有任何对象的信息,只有类的信息。但是虚函数可以是另一个类的友元函数

5、内敛函数不能是虚函数。

因为内敛函数不能在运行时动态确定位置。所以虚函数都是非内敛的。即使虚函数在类内部定义,在编译时,仍被看做为非内敛的。

6、构造函数不是虚函数。因为构造时,对象还不存在。

7、析构函数是可以虚函数,那么派生而来的所有派生类的析构函数也是虚函数,不管是否使用了关键字virtual

8、一旦一个函数被说明为虚函数,不管经历多少层派生,都将保持其虚特性,即虚特性具有继承性

9、如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。

10、虚函数在派生类中重新定义时,可以函数原型必须完全相同,如具有相同的形参个数和形参类型,但返回值可以不同。

 

纯虚函数:如果基类的函数没有必要或者无法实现,完全要依赖子类去实现的话,可以使用纯虚函数

简单点说,基类提供接口,而不提供定义,派生类提供实现

语法:

virtual 函数返回值类型 虚函数名(形参表)=0;

注意:从基类继承来的纯虚函数,在派生类中仍是纯虚函数,除非派生类按原型一致地超载该纯虚函数,变成类的纯虚函数

 

抽象类:包含了纯虚函数的类

注意:抽象类只能用作其他类的基类,不能建立对象,不能作为参数类型、返回类型或显式转换类型,可声明指针和引用。可以定义返回引用。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您介绍一下关于C++多态虚函数的实验。 首先,多态是指不同对象以不同的方式响应相同的消息的能力。在C++中,通过虚函数来实现多态虚函数是指在基类中被声明为虚函数的函数,在派生类中被重写后,在程序中使用基类指针或引用调用该函数时,会调用派生类中的函数。 下面是一个简单的多态虚函数的实验示例: ```c++ #include<iostream> using namespace std; class Shape { public: virtual void draw() { cout << "Drawing Shape" << endl; } }; class Circle: public Shape { public: void draw() { cout << "Drawing Circle" << endl; } }; class Square: public Shape { public: void draw() { cout << "Drawing Square" << endl; } }; int main() { Shape *s; Circle c; Square sq; s = &c; s->draw(); s = &sq; s->draw(); return 0; } ``` 运行结果为: ``` Drawing Circle Drawing Square ``` 在上述示例中,我们定义了一个基类Shape和两个派生类Circle和Square,分别重写了基类的虚函数draw()。在程序中,我们先定义了一个基类指针s,然后将其指向第一个派生类对象c,再调用s的虚函数draw(),此时会调用派生类Circle的draw()函数,因为指针实际上指向的是Circle类型的对象。接着,我们将指针s重新指向第二个派生类对象sq,再次调用s的虚函数draw(),此时会调用派生类Square的draw()函数。 通过这个实验,我们可以看到多态虚函数的特性,即通过基类指针或引用调用虚函数时,会根据实际对象的类型来调用相应的派生类函数。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值