假设没有Hello函数,我们只在main中调用ob.display,然后调用B类的显示函数而不是A类。
编译器将函数display()的调用设置为基类中定义的版本。这称为函数调用的静态解析,或静态链接 - 在执行程序之前修复函数调用。这有时也称为早期绑定,因为在编译程序期间设置了display()函数。
现在如何在基类中显示函数之前不使用虚拟关键字(后期绑定)来调用派生类的显示函数?
现在在这个程序中,将对象作为值调用传递,通过指针调用并通过引用调用Hello函数正常工作。现在,如果我们使用多态并想要显示派生类的成员函数,如果它被调用,我们必须在显示函数之前添加虚拟关键字。如果我们通过指针调用传递对象值并通过引用调用它调用派生类中的函数,但是如果我们按值传递对象,那么它不是为什么呢?>
class A
{
public:
void display(); // virtual void display()
{
cout << "Hey from A" <<endl;
}
};
class B : public A
{
public:
void display()
{
cout << "Hey from B" <<endl;
}
};
void Hello(A ob) // &ob //*ob
{
ob.display(); // ob->display()
}
int main()
{
B obj;
Hello(obj); // obj //&ob
return 0;
}
答案 0 :(得分:4)
现在如何在基类中显示函数之前不使用虚拟关键字(后期绑定)来调用派生类的显示函数?
编译器根据调用它的对象(或引用或指针)的静态类型简单地解析非虚函数。因此给定了派生类型的对象,以及对其子对象的引用:
B b;
A & a = b;
调用非虚函数会得到不同的结果:
b.display(); // called as B
a.display(); // called as A
如果你知道真实的类型,那么你可以指定你想要调用那个版本:
static_cast<B&>(a).display(); // called as B
但如果a
引用的对象没有B
类型,则会出现严重错误。
现在如果我们使用多态并想要显示派生类的成员函数,如果它被调用,我们必须在显示函数之前添加虚拟关键字。
正确。如果将函数设置为virtual,则根据对象的动态类型在运行时解析它,即使您使用不同类型的引用或指针来访问它。因此,上述两个示例都将其称为B
。
如果我们通过指针调用传递对象值并通过引用调用它调用派生类中的函数,但是如果我们按值传递对象,那么它不是为什么呢?
如果你按值传递它,那么你切片它:只复制对象的A
部分,以创建一个A
类型的新对象。因此,无论该函数是否为虚拟函数,在该对象上调用它都会选择A
版本,因为它只是A
而只有A
。
如果您通过引用或指针传递,那么您仍然使用其动态类型B
访问原始对象。因此,虚函数调用将解析为B
版本。
答案 1 :(得分:0)
现在如何在基类中显示函数之前不使用虚拟关键字(后期绑定)来调用派生类的显示函数?
您可以提供像这样的模板化基类
template<typename Derived>
class A {
public:
void display() {
// call Derived first
static_cast<Derived*>(this)->display();
cout << "Hey from A" <<endl;
}
};
class B : public A<B> {
public:
void display() {
cout << "Hey from B" <<endl;
}
};
这是众所周知的模式,称为CRTP和静态多态。