028-虚函数
看这段代码
public class Entity {
public String getName() {
return "Entity";
}
}
public class Player extends Entity {
private final String mName;
public Player(String name) {
this.mName = name;
}
public String getName() {
return mName;
}
}
public class Main {
public static void main(String[] args) {
Entity entity = new Entity();
System.out.println(entity.getName());
Player player = new Player("Player");
System.out.println(player.getName());
Entity pn = player;
System.out.println(pn.getName());
}
}
我们在Java中写下这样的代码,因为多态,我们运行的时候,即使父类引用指向子类的实例,调用方法的时候,依然会调用子类的实现
Entity
Player
Player
进程已结束,退出代码为 0
当我们在C++中写下这样的代码
#include <iostream>
class Entity {
public:
std::string getName() {
return "Entity";
}
};
class Player : public Entity {
private:
std::string mName;
public:
Player(const std::string &name) : mName(name) {}
std::string getName() {
return mName;
}
};
int main() {
Entity *entity = new Entity();
std::cout << entity->getName() << std::endl;
Player *player = new Player("Player");
std::cout << player->getName() << std::endl;
Entity *pn = player;
std::cout << pn->getName() << std::endl;
return 0;
}
运行的时候,结果是这样的
F:\Projects\ClionProjects\test\cmake-build-debug-cygwin\test.exe
Entity
Player
Entity
这就出问题了,因为我们声明函数的时候,通常是在类的内部起作用,调用的时候,会调用属于这种类型的方法
于是,出现了虚函数,引入了一种叫动态联编(Dynamic Dispatch)的东西,他通常使用v表来实现编译,v表是一个表,他包含基类所有虚函数的映射,在运行的时候,将它们映射到正确的覆写函数上,这里,我们只需要知道,需要覆写函数的时候,必须将基类的基函数声明为虚函数,要不会出错。
声明方式就是,添加virtual即可,同时,我们可以为覆写的方法添加override,(类似Java的注解?)
class Entity {
public:
virtual std::string getName() {
return "Entity";
}
};
class Player : public Entity {
private:
std::string mName;
public:
Player(const std::string &name) : mName(name) {}
std::string getName() override {
return mName;
}
};
使用虚函数有两点额外开销
1.需要额外的内存来保存v表,基类中还有一个额外的成员指针,指向v表
2.每次调用的时候,都需要遍历v表,来确定需要映射哪个函数
其实这两点实际使用中应该没啥影响...