虚函数:允许我们在子类中重写方法
例子:假设我们有两个类A和B,B是A派生出来的,即B是A的子类。如果我们在A类中创建一个方法,标记为Virtual,我们可以选择在B类中重写那个方法,让它做其他的事情。
虚函数引入了一种叫做动态联编(Dynamic Dispatch)的东西,它通常通过V表(虚函数表)来实现编译,V表就是一个表,它包含基类中所有虚函数的映射,这样在运行时就可以将它们映射到正确的覆写(override)函数。
如果想覆写一个函数,必须将基类中的基函数标记为虚函数。
有两种与虚函数相关的运行时成本
1、我们需要额外的内存来存储V表,这样我们就可以分配到正确的函数,包括基类中要有一个成员指针指向V表
2、每次我们调用虚函数时,我们需要遍历这个表,来确定要映射到哪个函数,这是额外的性能损失
# include <iostream>
# include <string>
using namespace std;
class Entity
{
public:
virtual string GetName() { return "Entity"; } // string用来定义返回字符串对象的函数
};
class Player : public Entity
{
private:
string m_Name;
public:
// 构造函数
Player(const string& Name)
: m_Name(Name) { }
string GetName() override { return m_Name; } //overrride 不是必须的,但是写上会让它有可读性,并帮助我们预防bug
};
void PrintName(Entity* e)
{
cout << e->GetName() << endl;
}
int main()
{
Entity* e = new Entity();
//cout << e->GetName() << endl;
PrintName(e);
// 箭头操作符:“->”,相当于先解引用指针,然后使用点操作符来访问对象的成员
Player* p = new Player("XIAO");
//cout << p->GetName() << endl;
PrintName(p);
// 为什莫打印到控制台的结果是相同的,因为在我们通常声明函数时,我们的方法通常在类内部起作用,然后当要调用方法时,会调用属于该类型的方法,而且如果我们看看这个PrintName函数,它的参数是Entity
cin.get();
return 0;
}
箭头操作符:“->”,相当于先解引用指针,然后使用点操作符来访问对象的成员
在我们通常声明函数时,我们的方法通常在类内部起作用,然后当要调用方法时,会调用属于该类型的方法,而且如果我们看看这个PrintName函数,它的参数是Entity,因此不用虚函数,控制台会引出相同的结果。