c++中的多态是在封装与继承的基础上实现的一个特性,其定义是在执行相同的函数时根据不同的对象实现不同的效果,例如有一个类Animal,派生出两个子类Cat与Dog,三个类都有eat()函数,当cat调用eat()函数时执行的是cat中的eat()方法,而Dog调用时则执行Dog中的eat()方法,这就是多态。
#include <stdlib.h>
#include <iostream>
using namespace std;
class Animal {//父类Animal
public:
Animal() {
cout << "Animal()" << endl;
}
~Animal()
{
cout << "~Animal()" << endl;
}
void eat() {
cout << "Animal eat" << endl;
}
};
class Cat :public Animal{//子类Cat
public:
Cat() {
cout << "Cat()" << endl;
}
~Cat()
{
cout << "~Cat()" << endl;
}
void eat() {
cout << "Cat eat" << endl;
}
};
class Dog :public Animal {//子类Dog
public:
Dog() {
cout << "Dog()" << endl;
}
~Dog()
{
cout << "~Dog()" << endl;
}
void eat() {
cout << "Dog eat" << endl;
}
};
int main(void) {
Cat *a1 = new Cat();
Dog *a2 = new Dog();
a1->eat();//分别调用eat()方法
a2->eat();
delete a1;
a1 = NULL;
delete a2;
a2 = NULL;
return 0;
}
结果如图:
1、虚函数
但是如果用Animal指针创建Dog与Cat类,则会执行Animal中的eat()方法,如下
int main(void) {
Animal *a1 = new Cat();//用Animal指针创建Cat
Animal *a2 = new Dog();//用Animal指针创建Dog
a1->eat();//分别调用eat()方法
a2->eat();
delete a1;
a1 = NULL;
delete a2;
a2 = NULL;
return 0;
}
运行结果显示Animal eat如图:
如果希望Cat与Dog分别执行对应的eat函数可以在父类的eat()函数前加关键字virtual,eat()函数便成为了虚函数,子类则会执行各自的eat()函数。
虚函数是一个指向函数的指针,占四个字节大小的空间。它就类似于一个空壳子,没有实际的函数,而是指向子类的eat()函数,当调用Animal类的eat()函数时,它会执行所指向的子类的eat()函数。
2、虚继承
当子类继承的两个父类有一个共同的祖先类时,两个父类都继承了祖先类的数据与方法,因此子类在继承父类时会继承两份祖先类的成员数据,造成内存浪费:
解决此问题可以在类继承时使用关键字virtual:
例如上面的例子中Cat类与Dog类,虚继承为:class Cat:virtual public Animal,class Dog:virtual public Animal,
子类Catdog继承自两个类class Catdog :public Cat,public Dog
3、虚析构函数
例如下面的程序,父类Shape,子类Circle继承自Shape,在Circle类中数据成员有int类型的长度,以及Point类表示点,在构造函数Circle时申请内存用于保存Point对象,并在析构函数中销毁指针。主函数中利用父类Shape实例化Circle类,并销毁它。
#include <stdlib.h>
#include <iostream>
using namespace std;
class Shape//新建父类Shape
{
public:
Shape()
{
cout << "Shape()" << endl;
}
~Shape()
{
cout << "~Shape()" << endl;
}
};
class Point {//新建点Point类
public:
Point(int x,int y) {
m_iX = x;
m_iY = y;
cout << "Point()" << endl;
}
~Point()
{
cout << "~Point()" << endl;
}
private:
int m_iX;
int m_iY;
};
class Circle :public Shape//新建圆Circle类继承自Shape
{
public:
Circle(int x,int y,int l)
{
p = new Point(x, y);//从堆中申请内存创建点Point对象
m_iLength = l;
cout << "Circle()" << endl;
}
~Circle()
{
delete p;//销毁指针p,归还内存
p = NULL;
cout << "~Circle()" << endl;
}
private:
Point *p;
int m_iLength;
};
int main(void) {
Shape *s = new Circle(1,2,3);//用Shape指针实例化一个Circle对象
delete s;//销毁指针
s = NULL;
return 0;
}
结果如图:
从结果中可以看到没有运行Circle类的析构函数,而是只运行了父类Shape的析构函数,从而其中的指针也无法释放,可能会造成内存泄漏。
利用虚析构函数可以避免这一点,在父类Shape的析构函数前加关键字virtual:virtual ~Shape()。程序在运行时便会调用子类的析构函数~Circle(),再调用父类自身的析构函数,从而释放内存。
4、RTTI(Run-Time Type Information)
通过运行时类型信息程序能够使用父类的指针或引用来检查这些指针或引用所指的对象的实际类型,从而执行不同操作。还可以将父类指针转换为子类对象指针。
#include <iostream>
#include <stdlib.h>
#include <string>
#include <typeinfo>
using namespace std;
class Movable//定义移动类:Movable
{
public:
virtual void move()=0;//纯虚函数:move
};
class Bus : public Movable//定义公交车类:Bus公有继承移动类
{
public:
virtual void move()
{
cout << "Bus -- move" << endl;
}
void carry()
{
cout << "Bus -- carry" << endl;
}
};
class Tank :public Movable//定义坦克类:Tank公有继承移动类
{
public:
virtual void move()
{
cout << "Tank -- move" << endl;
}
void fire()
{
cout << "Tank -- fire" << endl;
}
};
void doSomething(Movable *obj)
{
obj->move();
if(typeid(*obj)==typeid(Bus))//使用typeid判断指针对应的类型
{
Bus *bus=dynamic_cast<Bus*>(obj);//使用dynamic_cast将父类Movable *obj转换为Bus类
bus->carry();
}
if(typeid(*obj)==typeid(Tank))
{
Tank *tank=dynamic_cast<Tank*>(obj);
tank->fire();
}
}
int main(void)
{
Bus b;
Tank t;
doSomething(&b);//分别对传入不同类型的指针执行不同操作
doSomething(&t);
return 0;
}
运行结果如下,对tank与bus类对象分别执行了不同操作: