[英]Strange behavior with virtual functions
使用以下代码,我希望输出为Bf Bf DD.f,但是我得到的输出为Bf Bf Bf当DD
从具有f
为虚数的D
派生时,这怎么可能。
class B
{
public:
void f() { cout << "B.f "; }
};
class D : public B
{
public:
virtual void f() { cout << "D.f "; }
};
class DD : public D{
public:
virtual void f() { cout << "DD.f "; }
};
B * b = new B();
B * d = new D();
B * dd = new DD();
b->f();
d->f();
dd->f();
函数从被声明为virtual
的级别开始变为virtual
。 您首先在D
声明f
virtual
,这意味着动态分配将仅从D
向上进行。 B
没有,也不应该知道派生类。
考虑一下编译器如何看待它:
您有一个指向B
的指针B
具有以下定义:
class B
{
public:
void f() { cout << "B.f "; }
};
由于f
不是virtual
,所以我将继续静态解决该调用-即B::f()
。
为了使动态分配从指向B
的指针开始工作,您需要在B
中将f()
虚拟化:
class B
{
public:
virtual void f() { cout << "B.f "; }
};
您需要将B::f()
设置为虚拟,而不将B::f()
为虚拟,它不会出现在B虚拟表(vtbl)中,因此调用B::f()
而不是调度调用派生类。
class B
{
public:
virtual void f() { cout << "B.f "; }
};
在B中将f()声明为虚拟后,这便开始维护一个虚拟表,该表包含具有相同名称的派生类的所有其他函数的函数指针。 这是一个查找表,用于以动态/后期绑定方式解析函数调用。
当您使用引用或指针来调用方法时,编译器会在该方法的声明中搜索指针或引用的类型(此处,它在B
搜索带有签名f()
的某个方法的声明)。 当找到一个:
virtual
,则将其解决为对该类定义的方法的调用-这是静态绑定。 virtual
,则调用的方法将是所引用或指向的对象中的合适对象之一-这是动态绑定。 下一个测试应该是:
DD * dd = new DD();
D * d = dd;
B * b = d;
b->f();
d->f();
dd->f();
一个单独的对象new DD()
的使用/查看方式有所不同...每种类型都可以认为是您对对象的一种查看。 如果将其视为B
则f()
执行某些操作,但始终是相同的,但是如果将其视为D
或DD
,则f()
执行不同的操作...
如果您在街上遇到某人,向他致敬的标准方法是打个招呼,但对于同一个人,当他遇到他的一个朋友时,他要么打个招呼! 或哟! :
class Person {
public:
void salute() { cout << "Hello" << endl; }
};
class Friend : public Person {
public:
virtual void salute() { cout << "Hi!" << endl; }
};
class RoomMate : public Friend {
public:
virtual void salute() { cout << "Yo!" << endl; }
};
void asACustomer(Person &p) {
p.salute(); // static binding, we need the standard politeness
}
void asAFriend(Friend &f) {
p.salute(); // dynamic binding, we want an appropriate message...
}
RoomMate joe;
asCustomer(joe);
asFriend(joe);
使用静态绑定,您可以在编译时知道调用哪个方法。 使用动态绑定,您将无法做到,只知道合适的绑定就可以了。 这是子类型多态性的关键点。
通常,将静态绑定和动态绑定混合用于方法时要小心。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.