对于虚函数,在声明虚拟时访问某些方法时会发生什么,何时不是

时间:2011-10-27 21:27:51

标签: c++ class inheritance object virtual

对于虚函数,当声明虚拟时以及何时不访问某些方法时会发生什么。

我的想法:

如果它是虚拟的,则调用实例将在其虚函数表中搜索该函数,如果它是指向派生实例的基本实例指针,则将调用派生实例的虚函数实现。如果它不是虚函数,则调用调用实例的函数调用。

对吗?

2 个答案:

答案 0 :(得分:1)

从语言标准的角度来看,没有什么比你可能已经知道的更多:如果一个函数是虚函数,那么在任何基类引用或指针上调用它将导致调用最派生的实际函数。

就是这样。重要的是行为。

你真正想要的是编译器如何实现这一点。实际上,vtable是实现虚拟调度的最流行方式。它本质上是一个函数指针列表,它为每个具有虚函数的类维护。 (请记住,从具有虚函数的类派生会自动使这些函数在派生类中再次变为虚拟。)

但是,编译器实际调用函数的方式各不相同。

  • 如果函数不是虚函数,则在编译时已知,并静态调度到调用它的静态类类型的成员函数。

  • 如果函数是虚函数但编译器可以在编译时证明基本引用/指针的动态类型,它可以选择直接调用相应派生类的函数。

  • 如果编译器无法推断出动态类型,则函数调度会在运行时通过查找vtable中实际(派生的)函数的函数指针来发生。

示例:

struct A {
  void foo();
  virtual void bar();
};
struct B : A {
  void foo(); // hides A::foo() -- very bad style
  void bar(); // automatically virtual!!
};

int main() {
  B x;
  A * a1 = &x;  // pointer-to-base;
  A * a2 = get_pointer();

  a1->foo(); // static dispatch to A::foo() (non-virtual function)
  a1->bar(); // dispatch to B::foo(), possibly resolved statically
  a2->bar(); // dynamically dispatched to whatever the most derived class is
}

答案 1 :(得分:0)

右。如果通过基类指针调用virtual函数,则将调用覆盖。另一方面,如果通过基类指针调用非virtual函数,则将调用基类函数。

通过引用而不是指针调用时也是如此。

处理基类指针时要注意的一件事是slicing。如果你这样做:

Base* pb = new Der;
Base other = *pb;

other切割了对象,所有Der都消失了。