为什么我们不能在编译时查找指向的对象类型?

Why can't we look up the type of an object pointed at at compile-time?

本文关键字:对象 类型 查找 不能 我们 编译 为什么      更新时间:2023-10-16

我试图用C++来理解虚函数。

struct B {
  int f() { return 1;}
}
struct D : B {
  int f() { return 1;}
}

在主函数中:

B* b = new D;
b.f()

我的理解是基类和派生类之间存在"竞争":它们都有一个具有相同名称和相同签名的函数。调用b.f()时,只会选择其中一个。在虚拟案例中:

  • 获胜者是根据 B 指向的物体类型选出
  • 该选择是在运行时做出的

在非虚拟情况下:

  • 获胜者是根据指针 B 的类型选择
  • 该选择是在编译时做出的

我不明白两者之间的因果关系

  1. virtual关键字的使用
  2. 能够查找 B 所指向的对象类型
  3. 编译时与运行时

例如,为什么我们不能在编译时执行 (2)?

通常情况下,您无法在编译时知道类型。例如,考虑一个游戏,其中您有一些物理对象(实体),每个对象(实体)在接触时的行为可能不同。例如

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
   }
}

这是一个简单的例子

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),只要你做 (1),使用 virtual .你做 (2) 与typeid.一般来说,这不是性能最高的方法。(反馈效应:它很少有用,所以很少优化,这使得它更少使用,等等)。

依赖关系的原因是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&