C++中的早期绑定和晚期绑定 + 多态性

3
假设没有Hello函数,我们只在主函数中调用ob.display,那么它将调用类B的display函数而不是类A的。编译器会将display()函数的调用设置为基类中定义的版本,这被称为函数调用的静态分辨率或静态链接-函数调用在程序执行之前被固定。有时也称为早期绑定,因为display()函数在程序编译期间设置。那么在不使用虚拟关键字(后期绑定)之前如何调用派生类的display函数呢?
现在,在这个程序中,将对象作为值传递、指针传递和引用传递给Hello函数都可以正常工作。现在,如果我们使用多态性并希望在调用时显示派生类的成员函数,我们必须在基类的display函数之前添加虚拟关键字。如果我们通过指针和引用进行值的传递,则调用派生类中的函数,但如果我们通过值进行对象传递,则不会,为什么?
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;
} 

1
可能是对象切片是什么?的重复问题。 - Wintermute
2个回答

5
现在,在不使用virtual关键字(后期绑定)调用基类中显示函数的情况下,它如何调用派生类的显示函数呢?
非虚函数根据它所调用的对象(或引用或指针)的静态类型仅由编译器解析。因此,对于一个派生类型的对象和其子对象的引用,可以这样处理:
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,那么结果将会非常糟糕。

现在,如果我们使用多态并且想要显示派生类的成员函数(如果调用它),我们必须在基类中的 display 函数前添加 virtual 关键字。

正确。如果将函数设置为虚函数,那么它将根据对象的动态类型在运行时进行解析,即使您使用不同类型的引用或指针来访问它。因此,上述两个示例都会将其作为 B 调用。

如果我们通过指针和引用传递对象值并调用派生类中的函数,但如果我们通过值传递对象,则不会调用派生类中的函数,为什么?

如果您按值传递对象,则正在对其进行 切片处理:仅复制对象的 A 部分,以创建一个新的 A 类型对象。因此,无论函数是否为虚函数,在该对象上调用它都会选择 A 版本,因为它只是 A,而且仅仅是 A

如果通过引用或指针传递,则仍在访问原始对象,具有其动态类型 B。因此,虚函数调用将解析为 B 版本。


维基百科称对象切片发生是因为在超类中没有地方存储派生类的额外成员,因此被切掉。为什么如果我们通过引用或指针传递它,就不会发生对象切片?超类如何获得额外的空间来存储它? - Stack
1
@Stack:如果你传递一个引用或指针,你并没有创建一个新的对象。它是指已经存在的派生类型的对象,它已经有足够的空间来存储自己。 - Mike Seymour

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和静态多态性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接