多态
例如:实现图形库,可以用于显示多种图形
图形(位置/绘制)
/ \
矩形(长和宽/绘制) 圆形(半径/绘制)
函数重写(虚函数覆盖)、多态的概念
- 如果将基类中的某个成员函数声明为虚函数,那么其子类中与该函数具有相同原型的成员函数也就是虚函数,并且对基类版本形成覆盖,即函数重写。
- 此时,通过指向子类对象的基类指针,或引用子类对象的基类引用,调用虚函数,实际被执行的将是子类中的覆盖版本,而不再是基类中原始版本,这种语法现象称为多态。
函数重写要求(虚函数覆盖条件)
- 只有类中成员函数才能被声明为虚函数,全局函数、静态成员函数、构造函数都不能为虚函数。(析构函数可以为虚函数)
- 只有在基类中以 virtual 关键字修饰的成员函数才能作为虚函数被子类覆盖,而与子类中 virtual 无关。
- 虚函数在子类中的版本和基类中的版本要具有相同的函数签名,即函数名、参数表、常属性一致。
- 如果基类中的虚函数返回基本类型的数据,那么该函数在子类中的版本必须返回相同类型的数据。
- 如果基类中的虚函数返回的是类类型的指针(A*)或引用(A&),那么允许子类的版本返回其子类类型的指针(B*)或引用(B&)——类型协变。
class A{};
class B:public A{};
多态的条件
- 多态特性除了要满足虚函数覆盖,还必须通过指针或引用去调用虚函数才能表现出来。
- 调用虚函数的指针也可以是this指针,只要它是一个指向子类对象的基类指针,同样可以表现多态的语法特性。
例如:QT多线程的实现
class QThread{//QT官方定义表示多线程类
protected:
virtual void run(void){
//线程的入口函数
}
public:
void start(void){
this->run();
}
};
class MyThread:public QThread{
protected:
void run(void){
//需要放到线程中执行的代码
}
};
MyThread thread;
thread.start();
纯虚函数、抽象类和纯抽象类
- 纯虚函数
virtual 返回类型 函数名(形参表)[const] = 0;
- 抽象类
如果一个类中包含了纯虚函数,那么这个类就是抽象类,(注:抽象类不能创建对象) - 纯抽象类
如果一个类中所有的成员函数都是纯虚函数,那么这个类就是纯抽象类(有名接口类)
多态原理
通过虚函数表和动态绑定实现:
- 虚函数表会增加内存开销
- 动态绑定会增加时间开销
- 虚函数不能做内联优化
注:如果没用多态的语法要求,最好不用使用虚函数。
虚析构函数
- 基类的析构函数不会调用子类的析构函数,对一个指向子类对象的基类指针使用 delete 操作符,实际被调用的仅是基类的析构函数,子类的析构函数不会被调用,有内存泄漏的风险。
- 可以将基类中析构函数声明为虚函数,那么子类中的析构函数也就是虚函数,并且可以对基类中版本形成有效的覆盖,可以表现多态的语法特性;这时delete一个指向子类对象的基类指针,实际被执行的将是子类的析构函数,而子类析构函数在执行结束以后会自动调用基类的析构,避免了内存泄漏。
代码示例
- shape.cpp
#include <iostream>
using namespace std;
class Shape{
public:
Shape(int x=0,int y=0):m_x(x),m_y(y){}
virtual void draw(void){//虚函数
cout << "绘制图形:" << m_x <<
',' << m_y << endl;
}
protected:
int m_x;//坐标
int m_y;
};
class Rect:public Shape{
public:
Rect(int x,int y,int w,int h):
Shape(x,y),m_w(w),m_h(h){}
void draw(void){
cout << "绘制矩形:" << m_x << "," <<
m_y << "," << m_w << "," << m_h
<< endl;
}
private:
int m_w;
int m_h;
};
class Circle:public Shape{
public:
Circle(int x,int y,int r)
:Shape(x,y),m_r(r){}
void draw(void){
cout << "绘制圆形:" << m_x << "," <<
m_y << "," << m_r << endl;
}
private:
int m_r;
};
void render(Shape* buf[]){
for(int i=0;buf[i]!=NULL;i++){
//多态:根据指针实际指向的目标对象
//类型去调用虚函数,不再由指针本身
//类型决定
buf[i]->draw();
}
}
int main(void)
{
Shape* buf[1024] = {NULL};
buf[0] = new Rect(1,2,3,4);
buf[1] = new Rect(11,21,33,24);
buf[2] = new Circle(5,7,8);
buf[3] = new Rect(1,2,3,4);
buf[4] = new Circle(9,10,13);
buf[5] = new Rect(1,2,3,4);
render(buf);
return 0;
}
- 执行结果
- poly.cpp
#include <iostream>
using namespace std;
class Base{
public:
virtual int cal(int a,int b){
return a + b;
}
/* func是基类中成员函数,里面this指针类型
* 一定是Base*.
* 但是func函数的调用对象可以是子类对象,
* this又指向调用对象,这时this就是一个指
* 向子类对象的基类指针,再通过它去调用虚
* 函数,也可以表现多态的特性
* */
void func(void){
cout << cal(10,20) << endl;//200
}/*
void func(Base* this=&d){
cout << this->cal(10,20) << endl;
}*/
};
class Derived:public Base{
public:
int cal(int a,int b){
return a * b;
}
};
int main(void)
{
Derived d;
Base b = d;
cout << b.cal(10,20) << endl;
d.func();//func(&d)
return 0;
}
- 执行结果
- 02poly.cpp
#include <iostream>
using namespace std;
class Base{
public:
Base(void){
cout << "Base的构造函数" << endl;
}
//虚析构函数
virtual ~Base(void){
cout << "Base的析构函数" << endl;
}
};
class Derived:public Base{
public:
Derived(void){
cout << "Derived的构造函数" << endl;
}
~Derived(void){
cout << "Derived的析构函数" << endl;
}
};
int main(void)
{
Base* pb = new Derived;
//...
//pb->析构函数
delete pb;
}
- 执行结果
- typeid.cpp
#include <iostream>
#include <typeinfo>
using namespace std;
class A{ virtual void foo(void){} };
class B:public A{ void foo(void){} };
class C:public A{ void foo(void){} };
void func(A& ra){
if(typeid(ra) == typeid(B)){
cout << "针对B子类对象的处理"
<< endl;
}
else if(typeid(ra) == typeid(C)){
cout << "针对C子类对象的处理"
<< endl;
}
}
int main(void)
{
int i;
cout << typeid(int).name() << endl;//i
cout << typeid(i).name() << endl;//i
int* a1[10];
int (*a2)[10];
cout << typeid(a1).name()
<< endl;//A10_Pi
cout << typeid(a2).name()
<< endl;//PA10_i
cout << typeid(int (*[10])(char)).name()
<< endl;//A10_PFicE
B b;
func(b);
C c;
func(c);
return 0;
}
- 执行结果