多态是对于处理不同的变量,但是使用相同或者类似的方式。
多态主要分为两种形式:编译时多态(静态多态)和运行时多态(动态多态)
C++中多态通常使用虚函数或者指针(引用)实现
静态多态
函数重载
#include <iostream>
using namespace std;
int main()
{
//同样的函数 但是传入的参数不同
Test();
Test(1);
return 0;
}
void Test() {
}
//函数重载
void Test(int a) {
}
- 运算符重载
动态多态
虚函数
向上转型
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
~Person()
{
}
string name = "123";
void Test()
{
cout << "P测试" << endl;
}
};
class Student : public Person
{
public:
string name;
void Test()
{
cout << "S测试" << endl;
}
};
class Teacher : public Person
{
void Test()
{
cout << "T测试" << endl;
}
};
int main()
{
Student s;
Teacher t;
//向上转型
Person* p = &s;
p->Test();
p = &t;
p->Test();
return 0;
}
C++中子类的指针对象可以赋值给基类的指针对象是C++支持多态性,这种行为被称为向上转型。因为派生类包含了基类的所有对象,因此可以被视为基类的对象。
这样的向上转型,可以通过基类去调用派生类中与基类相同的函数,但是调用的还是基类Person的函数,想要调用子类的函数,需要在基类函数前添加关键字virtual
virtual void Test()
动态多态通过虚函数和虚函数表实现的,根据对象的实际类型来调用相应的虚函数,而不是根据指针或引用的静态类型。
虚函数表
虚函数的实现依赖于虚函数表,每一个包含虚函数的类都有一个虚函数表,表中存储了虚函数的地址,通过基类调用虚函数时,程序会在运行时查找虚函数表以找到正确的函数地址。
virtual 本质是构造指针以及指针的寻址地址
纯虚函数
//纯虚函数
virtual void Test() = 0;
如果一个类包含了一个纯虚函数,那么这个类就是抽象类
- 抽象类无法被实例化,只能作为基类去继承
- 抽象类的虚函数必须由派生类去实现
- 一个类如果继承多个抽象类,每个抽象类的抽象函数都要被实现
虚析构函数
class Person
{
public:
Person()
{
cout << "Person constructor" << endl;
}
//虚析构函数
virtual ~Person()
{
cout << "Person destructor" << endl;
}
};
栈式调用 堆式调用
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
}
~Person()
{
cout << "P析构" << endl;
}
string name = "123";
void Test()
{
cout << "P测试" << endl;
}
};
class Student : public Person
{
public:
string name;
~Student()
{
cout << "S析构" << endl;
}
void Test()
{
cout << "S测试" << endl;
}
};
class Teacher : public Person
{
public:
~Teacher()
{
cout << "T析构" << endl;
}
void Test()
{
cout << "T测试" << endl;
}
};
int main()
{
Student s;
Teacher t;
//栈式调用
Person* p = &s;
p->Test();
//堆式调用
//这样的调用是否还会调用Student的析构函数?
Person* p1 = new Student;
p1->Test();
delete p1;
return 0;
}
运行之后发现,栈式调用是会调用子类的析构函数的(也就是Student的析构),但是堆式调用时不会调用子类的析构函数的,但是如果不调用,那么就有可能产生内存泄漏。
为了解决这样的问题,我们使用虚析构函数,此时,就能够调用子类的析构函数。
当一个类有虚函数时,通常应该为他提供一个虚析构函数,以确保删除指向派生类对象的基类指针时正确调用析构函数