我正在学习虚函数,我对以下程序的结果非常困惑
我希望a1.virFun(b1)
和b1.virFun(b1)
都应该返回“来自B的问候”,但程序返回“来自A的问候”。这与我的理解相反。你能否解释为什么b1.sayHello()
没有被调用,即使我将b1作为参数传递而b1.sayHello()
是虚函数。
#include<iostream>
using namespace std;
class A
{
public:
virtual void sayHello();
void virFun(A obj);
};
class B : public A
{
public:
void virFun(A obj);
virtual void sayHello();
};
void A::sayHello()
{
cout << "hello from A" << endl;
}
void B::sayHello()
{
cout <<"hello from B" << endl;
}
void A::virFun(A obj)
{
obj.sayHello();
}
void B::virFun(A obj)
{
obj.sayHello();
}
int main()
{
A a1;
B b1;
a1.virFun(b1);
b1.virFun(b1);
return 0;
}
答案 0 :(得分:6)
void virFun(A obj);
要使虚函数工作,您需要通过引用或指针传递对象,以确保您始终使用原始对象。
void virFun(const A &obj);
void virFun(const A *obj);
否则virFun()
将收到您对象的副本,最终会"slicing"并丢失派生类的信息。该副本具有A
的vtable,并且缺少B
的额外字段。这通常是一场灾难。
作为一般规则,使用T&
或const T&
而不是普通T
传递对象。除了切片问题,它也更有效率。
答案 1 :(得分:3)
让virFun按值引用,而不是A对象:
void A::virFun(A& obj) { obj.sayHello(); }
原因:如果按值
A
取值,则会通过复制传递的值来初始化参数。如果您通过B,它将复制&#39; A&#39;将其中的一部分转换为参数类型(A
)的新变量 - 这称为 object slicing 。该参数将不再是&#39; be-a&#39;B
实例,因此它将表现为A
实例,好吧。
无需在B类中覆盖virFun,因为它在两种情况下都使用参数obj
事实上,virFun可能是一个静态函数。该名称令人困惑,因为它甚至不是virFun(虚函数) - 它是使用虚拟函数的常规函数。
#include<iostream>
using namespace std;
struct A { virtual void sayHello() { cout << "hello from A" << endl; } };
struct B : A { /*virtual*/ void sayHello() { cout << "hello from B" << endl; } };
static void virFun(A& obj)
{
obj.sayHello();
}
int main()
{
A a1;
B b1;
virFun(a1);
virFun(b1);
}
在http://liveworkspace.org/code/1624496ced29eb4683f5b19072f72f60
上查看hello from A
hello from B