【问题标题】:Early and late binding + polymorphism in c++C ++中的早期和晚期绑定+多态性
【发布时间】:2023-04-03 22:10:02
【问题描述】:

假设没有Hello函数,我们只是在main中调用ob.display,然后它调用B类而不是A类的显示函数。

函数 display() 的调用被编译器设置为基类中定义的版本。这称为函数调用的静态解析或静态链接 - 函数调用在程序执行之前就已固定。这有时也称为早期绑定,因为 display() 函数是在程序编译期间设置的。

现在如何在基类的显示函数前不使用virtual关键字(后期绑定)来调用派生类的显示函数呢?

现在在这个程序中,通过值调用、指针调用和对 Hello 函数的引用调用传递对象都可以正常工作。现在,如果我们使用多态,并且想要显示派生类的成员函数,如果它被调用,我们必须在基类的显示函数之前添加 virtual 关键字。如果我们通过指针调用和引用调用传递对象值,它会调用派生类中的函数,但如果我们通过值传递对象,为什么会这样?>

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;
} 

【问题讨论】:

标签: c++ binding polymorphism


【解决方案1】:

现在如何在基类的显示函数前不使用virtual关键字(后期绑定)来调用派生类的显示函数呢?

你可以像这样提供一个模板化的基类

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

【讨论】:

    【解决方案2】:

    现在如何在基类的显示函数前不使用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 类型,那将会非常错误。

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

    正确。如果您将函数设为虚拟,则它会在运行时根据对象的动态类型解析,即使您使用不同类型的引用或指针来访问它。所以上面两个例子都称它为B

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

    如果你通过值传递它,那么你切片它:只复制对象的A 部分,以创建A 类型的新对象。因此,无论该函数是否为虚拟函数,在该对象上调用它都会选择 A 版本,因为它是一个 A,而只是一个 A

    如果您通过引用或指针传递,那么您仍在访问原始对象,其动态类型为B。所以虚函数调用会解析为B版本。

    【讨论】:

    • wikipedia 说对象切片发生是因为在超类中没有地方存储派生类的其他成员,所以它被切掉了。如果我们通过引用或指针传递它,为什么不会发生对象切片?超类如何获得额外的空间来存储它?
    • @Stack:如果传递引用或指针,则不会创建新对象。它引用一个现有的对象,派生类型,它已经有足够的空间来存储自己。
    猜你喜欢
    • 2010-10-03
    • 2012-06-12
    • 2013-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    相关资源
    最近更新 更多