什么是多态
用一句话来概括多态:允许父类的指针指向子类对象。
为什么要用父类的指针去指向子类对象呢?我们想象一个最简单的场景,如果我们需要一个函数的参数是可变数据类型,那如何实现呢?C++是不允许模糊数据类型存在的,这个需求听起来几乎不可能实现。不过有了多态,我们可以把参数类型设置为父类的指针类型,这样在参数传递的时候我们就可以传递这个父类的任意一个子类的对象了。听起来比较乱,我们用一个例子来解释。
先看看下面这段代码:
#include <iostream>
using namespace std;
class Animal
{
public:
virtual void Run() = 0;
virtual void Cry() = 0;
};
class Dog : public Animal
{
void Run()
{
cout << "Dog is running ..." << endl;
}
void Cry()
{
cout << "Dog is crying ..." << endl;
}
};
class Cat : public Animal
{
void Run()
{
cout << "Cat is running ..." << endl;
}
void Cry()
{
cout << "Cat is crying ..." << endl;
}
};
void Functions(Animal* pAnimal)
{
pAnimal->Run();
pAnimal->Cry();
}
int main()
{
Dog dog;
Cat cat;
Functions(&dog);
Functions(&cat);
}
运行结果:
先看Functions()函数,它的参数是一个Animal类指针,Animal是一个抽象类,派生了两个子类:Dog和Cat。在main函数中,我们可以很方便地把这两个子类的对象传给Animal指针。这就是多态。
多态的背后隐藏着伟大的设计模式思想,希望大家慢慢体会。
抽象类
Animal是个抽象类。它的特点是:
- 包含虚函数
- 不能实例化
为什么要用抽象类呢?它的意义仅仅是定义成员函数的外观。在这个例子中,我们定义了两个虚函数:Run()和Cry()。有了这样一个抽象类,我们才能保证Dog和Cat两个子类都拥有一模一样的两个成员函数。否则,Functions()函数就没法正常工作了。
星空问题的新需求
回到星空那段代码,假如我们这个项目需要A,B,C,三个人同时开发。A开发点状星星,B开发矩形的星星,C开发X形的星星。我们要确保三个人能同时开发,完成后能很容易的把三份代码加入项目中,该怎么办呢?
先分析一下代码:
class Star
{
public:
Star(){}
~Star(){}
void Init();
void Move();
protected:
void Draw();
void NewPos();
void Remove();
double m_x = 0;
int m_y;
double m_step;
int m_color;
};
Star类中,影响星星形状的成员函数是Draw()和Remove(),我们可以利用多态把这两个函数抽象出来成为一个抽象类,之后让A,B,C三个人分别实现一个子类,这样就OK了。
#include <graphics.h>
#include <time.h>
#include <conio.h>
#include<stdlib.h>
#define MAXSTAR 50 // 星星总数
class Star// 星星基类
{
public:
Star(){}
~Star(){}
void StarMove(Star* p);//星星移动函数
void InitStar();//星星初始化
void Init();//天空初始化
protected:
virtual void Draw();//画星星虚函数实现多态
virtual void Remove();//擦除星星函数
void NewPos();//星星新位置
int x ;
double y;
double step;
int color;
};
class RectStar :public Star//矩形星星类
{
public:
RectStar(){}
~RectStar(){}
protected:
virtual void Draw()
{
setfillcolor(color);
solidcircle(x, y, 3);
};
virtual void Remove()
{
clearcircle(x, y, 3);
};
};
class Xstar:public Star// X型星星
{
public:
Xstar() {};
~Xstar() {};
protected:
virtual void Draw()
{
settextcolor(color);
outtextxy(x, y, _T("X"));
}
;
virtual void Remove()
{
settextcolor(0);
outtextxy(x, y, _T("X"));
};
};
void Star::InitStar()
{
x = 0;
y = rand() % 480;
step = rand()%5000/1000 + 1;
color = int (step * 255 / 6 + 0.5);
color = RGB(color, color, color);
}
void Star::StarMove(Star* p)
{
this->Remove();
NewPos();
this->Draw();
}
void Star::Init()
{
x = rand() % 640;
y = rand() % 480;
step = rand() % 5000 / 1000 + 1;
color = int(step * 255 / 6 + 0.5);
color = RGB(color, color, color);
putpixel((int)x, y, color);
}
void Star::Draw()
{
putpixel((int)x, y, color);
setcolor(color);
circle(x, y, 1);
}
void Star::NewPos()
{
x += step;
if (x > 640)
this->InitStar();
}
void Star::Remove()
{
putpixel((int)x, y, 0);
setcolor(0);
circle(x, y, 1);
}
int main()
{
srand((unsigned)time(NULL));//随机种子
initgraph(640,480);
Star star[MAXSTAR];
RectStar rectstar[MAXSTAR];
Xstar xstar[MAXSTAR];
for (int i = 0; i < MAXSTAR; i++)
{
star[i].Init();
rectstar[i].Init();
xstar[i].Init();
}
while (!_kbhit())//有键盘操作退出
{
for (int i = 0; i < MAXSTAR; i++)
{
star[i].StarMove(star);
rectstar[i].StarMove(rectstar);
xstar[i].StarMove(xstar);
}
Sleep(30);
}
closegraph();
}
执行结果:截了张图,画面星星是运动的