我有一个简单的代码作为多态和继承的例子
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,为什么?
答案 0 :(得分:4)
首先,你需要正确的术语!您没有重载任何功能,覆盖它们:
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
实例的切片副本。从本质上讲,这意味着B
中A
共有的所有属性都保留在此副本中,B
的所有属性都保留在B
中,而不是A
至DoSmthElse()
丢失了。因此,调用函数overloadedFunc()
的{{1}}内的对象现在完全属于A
类型(并且不再是B
类型),因此当然A::overloadedFunc()
1}}被调用。
在DoSmth
的第一种情况下,当参数是指向基类的类型指针时,可以获得正如您所期望的多态行为 - B*
参数传递给函数和副本此指针的类型为A*
。虽然副本已转换为A*
,但指向的对象仍为B
类型。因为指向的对象是B
类型的事实,它是通过指向它的基类的指针访问的,所以确保在行
B::overloadedFunc()
a->overloadedFunc();
已执行,因为虚拟函数overloadFunc()
被类B
覆盖。如果类B
未实现它自己的基类虚函数的不同版本(即类B
会覆盖类A
功能),则会调用基类版本。