【问题标题】:Why does base class pointer to a derived class object with overridden method call a base class method?为什么基类指针指向具有重写方法的派生类对象调用基类方法?
【发布时间】:2019-01-08 14:04:33
【问题描述】:
#include<iostream>
using namespace std;

class Base
{
public:
    void show() { cout<<" In Base \n"; }
};

class Derived: public Base
{
public:
    void show() { cout<<"In Derived \n"; }
};

int main(void)
{
    Base *bp = new Derived;
    bp->show(); // RUN-TIME POLYMORPHISM
    return 0;
}

在上面的代码中,show() 在基类中声明并在派生类中被覆盖。基类指针bp 指向派生类对象。现在,当bp 调用非虚拟show() 函数时。

输出:

In Base

但是,bp 指向派生类,那么为什么调用基类函数而不是派生类函数呢?

【问题讨论】:

标签: c++ inheritance virtual


【解决方案1】:

在c++中,即使你让基类指针指向派生类对象,它仍然从基类调用函数,这是因为早期绑定,所以为了实现后期绑定,把函数放在里面作为虚拟的基类。 即,虚拟 void show() { ………… }
现在,o/p 将是“In Derived”。

【讨论】:

    【解决方案2】:
    int main(void)
    {
        Base *bp = new Derived;
        bp->show(); // RUN-TIME POLYMORPHISM
        return 0;
    }
    

    编译器在编译时将它与基类对象绑定 “新派生”将对象传递给基,但它已与基对象绑定,它引用同一类 如果您添加虚拟绑定将在运行时发生一次,一旦派生对象传递它就会与驱动类绑定

    【讨论】:

      【解决方案3】:

      为了补充此处发布的其他答案,请参考此评论:

      我知道,但是基指针指向派生那为什么要调用基函数呢?

      请看这个帖子:https://stackoverflow.com/a/50830338/5743288

      因此,如果您要这样做:

      Derived *dp = new Derived;
      dp->show();
      

      你会得到以下输出:

      In Derived
      

      即使没有将show() 声明为virtual(因为编译器会知道你说过你想调用哪个方法)。

      因此,正如其他人指出的那样,如果您想要服务,您必须付出代价(尽管,即使使用您的原始代码,编译器也可能足够聪明以优化虚函数调用,请参阅here )。

      【讨论】:

        【解决方案4】:

        这里的核心问题是show 方法在派生类中没有被覆盖。从 C++11 开始,您可以使用 override 说明符来确保该方法确实会覆盖某些内容,因此编译器会为您检测到此问题:

        class Derived: public Base
        {
        public:
            void show() override { cout<<"In Derived \n"; }
        };
        

        prog.cc:13:10: 错误:'void Derived::show()' 标记为'覆盖',但没有覆盖

        为了覆盖一个方法,它应该在基类中声明为virtual

        class Base
        {
            public: virtual
            void show() { cout<<" In Base \n"; }
        };
        

        【讨论】:

        • 因提到 override 而被投票赞成,这是为这个确切的错误而发明的。
        【解决方案5】:

        大多数人已经回答说,您需要将函数声明为virtual,以便在代码执行时在运行时绑定。 我想补充一点,如果没有虚拟,要调用的方法是在编译时决定的,它将选择你声明的变量/指针类型的类的方法。在您的情况下,Base 类类型。

        另外,希望提供一个易于阅读的链接,它可以帮助您清除 C++ 中运行时多态性的概念:https://www.geeksforgeeks.org/virtual-function-cpp/

        【讨论】:

          【解决方案6】:

          这实际上就是所谓的运行时多态性。而在 C++ 中,它的程序员可以根据赋予基类指针的对象自行决定调用基类或派生类的所需函数。

          与指向任何派生类对象的基类指针无关。如果被调用的函数是非虚拟的,那么基类函数总是会被调用到一个基类指针。

          要从基类指针调用派生类函数,该函数必须标记为virtual

          【讨论】:

            【解决方案7】:

            // RUN-TIME POLYMORPHISM

            在 C++ 中,它是可选的。对于在运行时以多态方式解析的函数调用,程序员必须通过将其标记为virtual 来明确说明这是所需的。

            原因是动态调度永远不会没有成本,而 C++ 设计的一个主要特点是“你不需要为不需要的东西付费”。您必须说您确实需要它才能启用它。

            【讨论】:

              【解决方案8】:

              您需要将show() 设为虚拟以允许启动运行时多态性。

              virtual void show() { cout<<" In Base \n"; }
              

              【讨论】:

              • 我知道,但是基指针指向派生,那为什么要调用基函数呢?
              • @Jayesh 因为如果函数不是virtual,C++ 不会为Base 生成代码来调用Derived 的覆盖。使用运行时多态性对性能的影响很小,而且 C++ 的政策是不让程序支付超过程序员要求的费用。
              • @Jayesh 没有virtual 的绑定show() 并且它的主体没有延迟,因此没有多态性。
              • 另一个有趣的事实:当您将函数声明为virtual 时,派生类中该函数的所有覆盖都是virtual,除非调用final。还要研究并习惯使用override 关键字。捕捉这样的错误非常方便。
              • @Jayesh ,没有关键字“Virtual” Vptr 和 Vtable 从未被创建,与子类的构造函数的绑定从未发生,因此它保留在基类中。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2016-07-03
              • 2014-02-15
              • 1970-01-01
              • 1970-01-01
              • 2020-02-11
              • 1970-01-01
              • 2014-06-16
              相关资源
              最近更新 更多