我试图理解C ++中的虚函数。
struct B {
int f() { return 1;}
}
struct D : B {
int f() { return 1;}
}
在主要功能中:
B* b = new D;
b.f()
我的理解是基类和派生类之间存在“竞争”:它们都具有相同名称和相同签名的函数。调用b.f()
时,只会选择其中一个。在虚拟案例中:
在非虚拟案例中:
我不明白
之间的因果关系virtual
关键字为什么我们不能在编译时做(2)例如?
答案 0 :(得分:1)
通常情况下,您无法在编译时知道类型。例如,考虑一个游戏,其中有一些物理对象(实体),每个物体在接触时可能表现不同。 E.g。
struct Entity {
int x,y,w,h;
virtual void onPlayerContact() {}
};
struct ExitDoor : Entity {
void onPlayerContact() { exitLevel(); }
};
struct Monster : Entity {
void onPlayerContact() { diePlayer(); }
};
//...
现在,您将所有现有实体保存在一个大列表中,并在每个框架中,查看列表,检查您的播放器是否与实体联系,如果是,则调用onPlayerContact
。即:
static std::set<Entity*> entities;
static Player* player;
void frame() {
for(Entity* entity : entities) {
if(player->contacts(entity))
entity->onPlayerContact(); // it's only known at runtime what to call here
}
}
答案 1 :(得分:1)
这是一个简单的例子
void some_function(B* b)
{
b.f();
}
int main()
{
int i;
cin >> i;
if (i == 0)
{
B *b = new B();
some_function(b);
}
else
{
D *d = new D();
some_function(d);
}
return 0;
}
在编译时,您不知道传递给函数“some_function(B * b)”的对象的确切类型。它应该在运行时决定。
答案 2 :(得分:0)
如果你做(1),你可以做(2),使用virtual
。你用typeid
做(2)。一般来说,这不是最高效的方法。 (反馈效果:它很少有用,很少被优化,这使得它的使用更少,等等。)
依赖的原因是virtual
启用RTTI(运行时类型信息),这也是2所需的。一种常见的RTTI形式是所谓的vtable
,它是一个包含指向给定类的虚函数的指针的表。具有虚函数的每个对象都有一个指向该表的指针。 typeid
信息可以通过几种方式添加到这样的表中(嵌入,数据指针,返回函数的指针等)。但除了指向vtable
的指针之外,还有其他选项可以实现RTTI,所以不要认为它是唯一的方法。
你也想知道为什么你不能在运行时这样做。考虑以下代码
void Foo(std::ostream&);
int main(int argc, char **argv)
{
if (argc == 2)
{
std::ofstream outFile(argv[1]);
Foo(outFile);
}
else
{
Foo(std::cout);
}
}
显然,在编译Foo
时,您不知道在运行时会得到什么。这取决于命令行。在编译时,您只知道您将获得ostream&
。