多态

例如:实现图形库,可以用于显示多种图形

			   图形(位置/绘制)
			/ 				\
	矩形(长和宽/绘制) 	圆形(半径/绘制)

函数重写(虚函数覆盖)、多态的概念

  • 如果将基类中的某个成员函数声明为虚函数,那么其子类中与该函数具有相同原型的成员函数也就是虚函数,并且对基类版本形成覆盖,即函数重写。
  • 此时,通过指向子类对象的基类指针,或引用子类对象的基类引用,调用虚函数,实际被执行的将是子类中的覆盖版本,而不再是基类中原始版本,这种语法现象称为多态。

函数重写要求(虚函数覆盖条件)

  • 只有类中成员函数才能被声明为虚函数,全局函数、静态成员函数、构造函数都不能为虚函数。(析构函数可以为虚函数)
  • 只有在基类中以 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;
}

  • 执行结果
    C++——多态_子类
  • 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;
    
}
  • 执行结果
    C++——多态_多态_02
  • 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;

}

  • 执行结果
    C++——多态_虚函数_03
  • 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;
}

  • 执行结果
    C++——多态_子类_04