多态分为静态多态和动态多态
(看完这篇文章,可能会不认识态这个字)
静态多态:体现在运算符重载,函数重载等方面,即程序再编译的时候编译器来确定使用哪个函数,所以也称为编译时多态。
动态多态:程序在编译阶段无法确定调用哪个函数,只能在程序运行的时候选择一个正确的参数,这种多态被称为动态多态。
由于动态多态的基础是虚函数(在后面),所以我们一点一点的看。
指向基类的指针和引用,可以指向或引用派生类对象,而不需要进行强制类型转换。
举个例子:
B是A的派生类,那么可以这样写:
B b;
A* p = &b;//基类的指针
A& a = b;//基类的引用
这里用到了上行转换,等号右面的b转换成了A类的对象,然后使得式子成立。注意:上行转换不需要显式进行,但下行转换,一定要使用显式类型转换。
但是,上行转换后,指针只能访问派生类从基类继承的成员,不能访问派生类自己新增的成员。
为了实现上文提到的运行时多态,C++引入了虚函数。
虚函数的定义:
虚函数只能是类的非静态成员函数。
class Student{
private:
int num;
string name;
public:
Student(int n,string name):num(n),name(name){}
virtual void Test();//这里就是虚函数的定义
};
注意:
下面的例子可以看出在调用虚函数的时候如何使用。
#include
#include
using namespace std;
class Student //基类
{
public:
Student(const string& = "", const string& = "");//声明构造函数
virtual void Test(); //虚函数
protected:
string strNum, strName; //描述学生的学号和姓名
};
Student::Student(const string& number, const string& name)
{
strNum = number;
strName = name;
}
void Student::Test() //实现基类的虚函数Test()
{
cout << "My God! 又要考试啦!" << endl;
}
class Doctor : public Student //公有派生Doctor(博士生类)
{
public:
Doctor(const string&, const string&, const string&);
void Test() //声明虚函数Test(),原型与基类的虚函数完全一样
{//直接在类内实现虚函数
cout << "Doctor..." << endl;
}
private:
string strMajor; //专业信息
};
Doctor::Doctor(const string& number, const string& name, const string& major) : Student(number, name)
{
strMajor = major;
}
class MidschoolStu : public Student//派生类中学生类
{
public:
void Test(){cout << "MidschoolStu..." << endl;}
};
class HighschoolStu : public MidschoolStu //高中生类由中学生类派生
{
public:
void Test(){cout << "Highschoolstu..." << endl;}
};
int main()
{
Student* pStu = nullptr; //创建一个Student类型的空指针
Doctor ds("2019416001", "Ailsa", "Computer Science and Technology");
MidschoolStu ms;
HighschoolStu hs;
pStu = &ds;
pStu->Test();//这里调用的就是虚函数
pStu = &ms;
pStu->Test();
pStu = &hs;
pStu->Test();
return 0;
}
这里总结一下:
重载
隐藏
默认情况下,编译器只调用基类的析构函数回收基类的数据成员占用的资源。那么当在派生类构造函数中使用new运算符为派生类新增成员申请的内存回收的时候,就要用到虚析构函数。
把基类的析构函数声明为虚析构函数,然后定义派生类的析构函数,在派生类中用delete运算符回收内存。
class Student {
public:
Student(const string& = "", const string& = "");
virtual ~Student(){cout << "Student destructor is called..." << endl;}
protected:
string strNum, strName;
};
class Doctor : public Student{
public:
Doctor(const string&, const string&, double, double);
~Doctor()
{
cout << "Doctor destructor is called..." << endl;
delete[] dScore;
}
private:
double* dScore; //保存成绩信息
};
解析:这里的func1由于不是虚函数,所以指针a会调用基类中的func1(),所以先输出了A1,但这里的func2()是虚函数,所以会调用派生类中的func2(),这样,就可以看出运行时多态(基类指针指向派生类对象的时候,调用的是派生类中的函数)。
纯虚函数允许在基类中只给出虚函数的声明,不给出虚函数的实现。这样的纯虚函数只是为了给派生类提供一个接口,在派生类中实现,来实现运行时的多态。
纯虚函数的声明:
virtual void Test() = 0;
包含纯虚函数的类被称为抽象类,抽象类基于纯虚函数,并且只用作基类,不能声明抽象类的对象,但可以通过创建抽象类的指针或引用来操作派生类对象。(如果在派生类中纯虚函数没有实现,那么也不能声明此派生类的对象,因为此派生类也是抽象类。)
空指针(nullptr):
Student *p = nullptr;
这里p是定义的一个空指针。
继承、虚函数、指针和引用类型的兼容赋值