文章目录
多态
函数重写(虚函数覆盖)、多态的概念
函数重写要求(虚函数覆盖条件)
多态的条件
纯虚函数、抽象类和纯抽象类
多态原理
虚析构函数
代码示例
多态
例如:实现图形库,可以用于显示多种图形
图形(位置/绘制) / \ 矩形(长和宽/绘制) 圆形(半径/绘制)
函数重写(虚函数覆盖)、多态的概念
- 如果将基类中的某个成员函数声明为虚函数,那么其子类中与该函数具有相同原型的成员函数也就是虚函数,并且对基类版本形成覆盖,即函数重写。
- 此时,通过指向子类对象的基类指针,或引用子类对象的基类引用,调用虚函数,实际被执行的将是子类中的覆盖版本,而不再是基类中原始版本,这种语法现象称为多态。
函数重写要求(虚函数覆盖条件)
- 只有类中成员函数才能被声明为虚函数,全局函数、静态成员函数、构造函数都不能为虚函数。(析构函数可以为虚函数)
- 只有在基类中以 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; }
执行结果



