函数重载和虚方法表

时间:2012-11-21 19:51:07

标签: c++ overloading

  

可能重复:
  What is the slicing problem in C++?

我有一个简单的代码作为多态和继承的例子

class A
{
public:
    int fieldInA;

    void virtual overloadedFunc()
    {
        printf("You are in A\n");
    }
};

class B : public A
{
public:
    int fieldInB;

    void overloadedFunc()
    {
        printf("You are in B\n");
    }
};

void DoSmth(A* a)
{
    a->overloadedFunc();
}

void DoSmthElse(A a)
{
    a.overloadedFunc();
}
int _tmain(int argc, _TCHAR* argv[])
{
    B *b1 = new B();
    B b2;
    //Breakpoint here
    DoSmth(b1);
    DoSmthElse(b2);
    scanf("%*s");
    return 0;
}

当我在断点处停止时,b1的_vfptr [0]和b2的_vfptr [0]的值是相同的(SomeAddr(B :: overloadedFunc(void)))。 在doSmth()中传递b1作为参数后,局部变量a的_vfptr [0]仍然是someAddr(B :: overloadedFunc(void)),但是DoSmthElse中的_vfptr [0]现在是someAddr(A :: overloadedFunc(void) ))。我确信这是我对函数重载概念的误解,但我无法理解,为什么在第一种情况下我看到“你在B中”而在第二次“你在A中”。与A * b1 = new B()相同; DoSmth(B1); //你在B,为什么?

2 个答案:

答案 0 :(得分:4)

首先,你需要正确的术语!您没有重载任何功能,覆盖它们:

  • 重载意味着您具有相同的函数名称和不同类型的参数。选择正确的重载是编译时操作。
  • 覆盖意味着您具有带有多态(在C ++ virtual)函数中的类层次结构,并且您使用适用于更专业类的对象的函数替换正在调用的函数。你覆盖原意。选择正确的覆盖是一个运行时操作,在C ++中使用类似于虚函数表的东西。

这些术语容易混淆并且使事情变得更糟,这甚至会相互作用:在编译时选择正确的重载,最终调用虚函数,因此可以覆盖它。此外,覆盖派生类可能会隐藏从基类继承的重载。如果你不能直接得到这些条款,这一切都没有意义!

现在,针对您的实际问题,当您致电DoSmthElse()时,您将对象b2按值传递给采用A类型对象的函数。这会通过复制A对象的A子对象来创建类型为B的对象。但由于B来自A,因此并非所有B都会被表示,即您在A中看到的DoSmthElse()对象的行为并不像B对象,但与A对象相似。毕竟,它一个A对象,而不是B!此过程通常称为切片:您将B对象的部分切掉,使其变得特殊。

答案 1 :(得分:1)

要获得多态行为,您需要在指针或对基类的引用上调用虚函数,而不是基类的实例。当你调用这个函数时

void DoSmthElse(A a)

您传递了B的实例。这是按值传递的,因此参数是您传递给它的B实例的切片副本。从本质上讲,这意味着BA共有的所有属性都保留在此副本中,B的所有属性都保留在B中,而不是ADoSmthElse()丢失了。因此,调用函数overloadedFunc()的{​​{1}}内的对象现在完全属于A类型(并且不再是B类型),因此当然A::overloadedFunc() 1}}被调用。

DoSmth的第一种情况下,当参数是指向基类的类型指针时,可以获得正如您所期望的多态行为 - B*参数传递给函数和副本此指针的类型为A*。虽然副本已转换为A*,但指向的对象仍为B类型。因为指向的对象是B类型的事实,它是通过指向它的基类的指针访问的,所以确保在行

时实际调用函数B::overloadedFunc()
a->overloadedFunc();

已执行,因为虚拟函数overloadFunc()被类B 覆盖。如果类B未实现它自己的基类虚函数的不同版本(即类B会覆盖类A功能),则会调用基类版本。