多态是指同一名字的事物可以完成不同的功能。多态可以分为编译时的多态和运行时
的多态。前者主要是指函数重载、对重载函数的调用,在编译时就能根据实参确定应
该调用哪个函数。后者则和继承、虚函数等概念相关。
通过基类指针实现多态:
派生类对象的地址可以赋值给基类指针。对于通过基类指针调用基类和派生类中
都有的同名、同参数表的虚函数语句,编译时并不确定要执行的是基类还是派生
类的虚函数,而当程序运行到该语句时,如果基类指针指向一个基类对象,则基
类的虚函数被调用,如果基类的指针指向一个派生类对象,则派生类虚函数被调
用。这种机制就叫做多态。
通过基类引用实现多态:
通过基类的引用调用虚函数的语句也是多态的。
虚函数:
在函数声明前加上virtual关键字的成员函数。virtual关键字只在类定义中的成员函
数声明处使用,不能再类外部写成员函数体时使用。
静态成员函数不能是虚函数。
包含虚函数的类称为多态类。
多态的作用:
在面向对象的程序设计中,使用多态能够增加程序的可扩充性,即程序需要修改或增加功能
时,只需改动或增加较少的代码。此外,使用多态也能够起到精简代码的作用。
#include <iostream> #include <string> using namespace std; class fruit{ private: double price; public: virtual void Print(){ cout << "Base Class" << endl; } }; class apple:public fruit{ private: string area; public: virtual void Print(){ cout << "Derived Class" << endl; } }; int main() { fruit f; apple a; fruit *pf1 = &f; fruit *pf2 = &a; fruit &pf3 = a; pf1->Print(); pf2->Print(); pf3.Print(); return 0; } /* 输出结果: Base Class Derived Class Derived Class */
关于多态的主意事项:
类的成员函数之间而已互相调用。在成员函数(静态成员函数、构造函数和析构函数除外)中
调用其它虚成员函数的语句时多态的。
#include <iostream> using namespace std; class fruit{ public: void fun(){ Print(); } virtual void Print(){ cout << "Base Class" << endl; } }; class apple:public fruit{ public: virtual void Print(){ cout << "Derived Class" << endl; } }; int main() { apple a; a.fun(); // 输出:Derived Class // 由于a是一个apple类对象,所以调用的是apple中的Print函数 return 0; }
在构造函数和析构函数中调用虚函数:
在构造函数和析构函数中调用虚函数不是多态,因为编译时即可确定调用的是哪个函
数。如果本类中有该函数,调用的就是本类中的函数;如果本类中没有,调用的就是
直接基类的函数;如果直接基类中没有,就调用间接基类中的,以此类推。
注意区分多态和非多态的情况:
要注意,通过基类指针或引用调用成员函数的语句,只有当该成员函数时虚函数时才
会是多态。如果该成员函数不是虚函数,那么这条函数调用语句就是静态联编的,编
译时就能确定调用的时哪个类的成员函数。
C++规定,只要基类中某个函数被声明为虚函数,则派生类中的同名、同参数表的成员函数
即使前面不加virtual关键字,也自动成为虚函数。
虚析构函数:
有时我们会将基类指针指向一个用new运算符动态生成的派生类对象,在释放该对象时是
通过释放指向它的指针实现的。这时,由于delete语句时静态编联的,编译器在此处无
法此时这个指针是指向哪个对象的,只会根据指针是基类类型,而调用基类的析构函数,
从而造成程序的问题。这时,我们可以通过将基类的析构函数声明为虚函数来解决这个
问题。
#include <iostream> using namespace std; class fruit1{ public: ~fruit1(){ cout << "Base Class" << endl; } }; class apple1:public fruit1{ public: ~apple1(){ cout << "Derived Class" << endl; } }; class fruit2{ public: virtual ~fruit2(){ cout << "Base Class" << endl; } }; class apple2:public fruit2{ public: virtual ~apple2(){ cout << "Derived Class" << endl; } }; int main() { fruit1 *pf = new apple1; delete pf; fruit2 *p_f = new apple2; delete p_f; return 0; } /* 输出结果: Base Class Derived Class Base Class */
纯虚函数和抽象类:
纯虚函数就是没有函数体的虚函数。包含纯虚函数的类就叫做抽象类。
纯虚函数的写法就是在函数声明后加"=0"
抽象类不能独立生成对象。
抽象类的作用:
抽象类可以作为基类,用来派生新类。可以定义抽象类的指针或引用,并让它们指向或引
用抽象类的派生类的对象,为多态的实现提供条件。独立的抽象类的对象不存在,但是包
含在派生类对象中的抽象类的对象是可以存在的。
如果一个类从抽象类派生而来,当且仅当它对基类中的所有纯虚函数都进行覆盖并都写出函数
体,它才能成为非抽象类。
class fruit1{ // 抽象类 public: virtual void Print() =0; // 纯虚函数 };