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

时间:2013-10-21 07:38:09

标签: c++ virtual-functions

我试图理解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. 指向的对象的类型
  4. 编译时间与运行时间
  5. 为什么我们不能在编译时做(2)例如?

3 个答案:

答案 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&