C++中类的多态与虚函数的利用
副标题#e#
类的多态特性是支持面向工具的语言最主要的特性,有过非面向工具语言开拓经验的人,凡是对这一章节的内容会以为不习惯,因为许多人错误的认为,支持类的封装的语言就是支持面向工具的,其实否则,Visual BASIC 6.0 是典范的非面向工具的开拓语言,可是它简直是支持类,支持类并不能说明就是支持面向工具,可以或许办理多态问题的语言,才是真正支持面向工具的开拓的语言,所以务必提醒有过其它非面向工具语言基本的读者留意!
多态的这个观念稍微有点恍惚,假如想在一开始就想用清晰用语言描写它,让读者可以或许大白,好像不太现实,所以我们先看如下代码:
//例程1
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed=speed;
Vehicle::total=total;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<endl;
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird=aird;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
protected:
int aird;
};
void main()
{
Vehicle a(120,4);
a.ShowMember();
Car b(180,110,4);
b.ShowMember();
cin.get();
}
在c++中是答允派生类重载基类成员函数的,对付类的重载来说,明晰的,差异类的工具,挪用其类的成员函数的时候,系统是知道如何找到其类的同名成员,上面代码中的a.ShowMember();,即挪用的是Vehicle::ShowMember(),b.ShowMember();,即挪用的是Car::ShowMemeber();。
#p#副标题#e#
可是在实际事情中,很大概会遇到工具所属类不清的环境,下面我们来看一下派生类成员作为函数参数通报的例子,代码如下:
//例程2
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed=speed;
Vehicle::total=total;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<endl;
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird=aird;
}
void ShowMember()
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
protected:
int aird;
};
void test(Vehicle &temp)
{
temp.ShowMember();
}
void main()
{
Vehicle a(120,4);
Car b(180,110,4);
test(a);
test(b);
cin.get();
}
例子中,工具a与b判别是基类和派生类的工具,而函数test的形参却只是Vehicle类的引用,凭据类担任的特点,系统把Car类工具看做是一个Vehicle类工具,因为Car类的包围范畴包括Vehicle类,所以test函数的界说并没有错误,我们想操作test函数到达的目标是,通报差异类工具的引用,别离挪用差异类的,重载了的,ShowMember成员函数,可是措施的运行功效却出乎人们的料想,系统分不清楚通报过来的基类工具照旧派生类工具,无论是基类工具照旧派生类工具挪用的都是基类的ShowMember成员函数。
为了要办理上述不能正确判别工具范例的问题,c++提供了一种叫做多态性(polymorphism)的技能来办理问题,对付例措施1,这种可以或许在编译时就可以或许确定哪个重载的成员函数被挪用的环境被称做先期联编(early binding),而在系统可以或许在运行时,可以或许按照其范例确定挪用哪个重载的成员函数的本领,称为多态性,或叫滞后联编(late binding),下面我们要看的例程3,就是滞后联编,滞后联编正是办理多态问题的要领。
代码如下:
//例程3
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed = speed;
Vehicle::total = total;
}
virtual void ShowMember()//虚函数
{
cout<<speed<<"|"<<total<<endl;
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird = aird;
}
virtual void ShowMember()//虚函数,在派生类中,由于担任的干系,这里的virtual也可以不加
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
public:
int aird;
};
void test(Vehicle &temp)
{
temp.ShowMember();
}
int main()
{
Vehicle a(120,4);
Car b(180,110,4);
test(a);
test(b);
cin.get();
}
#p#分页标题#e#
多态特性的事情依赖虚函数的界说,在需要办理多态问题的重载成员函数前,加上virtual要害字,那么该成员函数就酿成了虚函数,从上例代码运行的功效看,系统乐成的判别出了工具的真实范例,乐成的挪用了各自的重载成员函数。
多态特性让措施员省去了细节的思量,提高了开拓效率,使代码大大的简化,虽然虚函数的界说也是有缺陷的,因为多态特性增加了一些数据存储和执行指令的开销,所以能不消多态最好不消。
虚函数的界说要遵循以下重要法则:
1.假如虚函数在基类与派生类中呈现,仅仅是名字沟通,而形式参数差异,可能是返回范例差异,那么纵然加上了virtual要害字,也是不会举办滞后联编的。
2.只有类的成员函数才气说明为虚函数,因为虚函数仅适适用与有担任干系的类工具,所以普通函数不能说明为虚函数。
3.静态成员函数不能是虚函数,因为静态成员函数的特点是不受限制于某个工具。
4.内联(inline)函数不能是虚函数,因为内联函数不能在运行中动态确定位置。纵然虚函数在类的内部界说界说,可是在编译的时候系统仍然将它看做长短内联的。
5.结构函数不能是虚函数,因为结构的时候,工具照旧一片位定型的空间,只有结构完成后,工具才是详细类的实例。
6.析构函数可以是虚函数,并且凡是声名为虚函数。
说明一下,固然我们说利用虚函数会低落效率,可是在处理惩罚器速度越来越快的本日,将一个类中的所有成员函数都界说成为virtual老是有长处的,它除了会增加一些特另外开销是没有其它弊端的,对付担保类的封装特性是有长处的。
对付上面虚函数利用的重要法则6,我们有须要用实例说明一下,为什么具备多态特性的类的析构函数,有须要声明为virtual。
代码如下:
#include <iostream>
using namespace std;
class Vehicle
{
public:
Vehicle(float speed,int total)
{
Vehicle::speed=speed;
Vehicle::total=total;
}
virtual void ShowMember()
{
cout<<speed<<"|"<<total<<endl;
}
virtual ~Vehicle()
{
cout<<"载入Vehicle基类析构函数"<<endl;
cin.get();
}
protected:
float speed;
int total;
};
class Car:public Vehicle
{
public:
Car(int aird,float speed,int total):Vehicle(speed,total)
{
Car::aird=aird;
}
virtual void ShowMember()
{
cout<<speed<<"|"<<total<<"|"<<aird<<endl;
}
virtual ~Car()
{
cout<<"载入Car派生类析构函数"<<endl;
cin.get();
}
protected:
int aird;
};
void test(Vehicle &temp)
{
temp.ShowMember();
}
void DelPN(Vehicle *temp)
{
delete temp;
}
void main()
{
Car *a=new Car(100,1,1);
a->ShowMember();
DelPN(a);
cin.get();
}
从上例代码的运行功效来看,当挪用DelPN(a);后,在析构的时候,系统乐成简直定了先挪用Car类的析构函数,而假如将析构函数的virtual修饰去掉,再调查功效,会发明析构的时候,始终只挪用了基类的析构函数,由此我们发明,多态的特性的virtual修饰,不光单对基类和派生类的普通成员函数有须要,并且对付基类和派生类的析构函数同样重要。