【问题标题】:Virtual keyword use in C++C++ 中虚拟关键字的使用
【发布时间】:2013-08-08 20:59:12
【问题描述】:

我知道 C++ 通过虚函数实现运行时多态性,并且 virtual 关键字是继承的,但我没有看到在派生类中使用 virtual 关键字。

例如在以下情况下,即使您在派生类中删除了 virtual 关键字,ptr->method() 调用仍然会转到派生::method。那么这个 virtual 关键字在派生类中有什么额外作用呢?

#include<iostream>

using namespace std;

class base
{
public:
    virtual void method()
    {
        std::cout << std::endl << "BASE" << std::endl;
    }
};

class derived: public base
{
public:
    virtual void method()
    {
        std::cout << std::endl << "DERIVED" << std::endl;
    }
};

int main()
{
    base* ptr = new derived();
    ptr->method();
    return 9;
}

【问题讨论】:

  • 如你所见,它是多余的。
  • @Nbr44 你不会从这个评论中回答
  • virtual 在基类中重写虚函数时隐含在派生类中。 C++11 还引入了final 关键字,它可以防止进一步的派生类重新实现虚函数。
  • @triclosan 确实如此。我贴出来了。

标签: c++ polymorphism virtual-functions


【解决方案1】:

什么都没有。只是为了帮助提醒您哪些功能是虚拟的。

【讨论】:

    【解决方案2】:

    virtual 仅在基类声明中是必需的。它在派生类中是可选的,在这些情况下可能主要用作提醒。

    C++11 引入了override 以使事情更加明确:它明确地将派生类中的方法标记为对基类的virtual 方法的覆盖。

    【讨论】:

      【解决方案3】:

      如果派生类的方法通过名称和签名与基类之一的虚拟方法匹配,并且匹配的方法是虚拟的,那么派生类的方法也变为虚拟。因此,从技术上讲,没有必要在派生类中将此类方法标记为“virtual”。然而,在 C++11 之前,它曾经是一个很好的实践,因为它对那些阅读代码的人来说是一个很好的提示(可能很难记住基类的所有虚函数)。

      从 C++11 开始,派生类中有两个额外的关键字可以帮助提高可读性和代码的健壮性。它们是«override»和«final»。例如,将«override»放在派生类的方法中可以确保基类的相应方法实际上是虚拟的。 «final» 关键字的作用相同,而且它可以防止该方法被进一步覆盖。

      我还在我的博客here 中写了更多真实世界的基本原理和代码示例。

      希望对您有所帮助。祝你好运!

      【讨论】:

      • 我喜欢你的博文,实际上我可能会开始使用 override/final。
      • final 特殊标识符override plus 不同...,它只禁止在派生类型中覆盖。虽然创建一个虚函数final 如果它不覆盖另一个函数(为什么要让它成为虚拟函数呢?)没有什么意义,但编译器不会验证它确实覆盖了任何东西,因为你需要结合finaloverride。也就是说,override 要求基类中有覆盖,final 意味着派生类型中不能有覆盖。一个抬头,一个向下。 ideone中的基础测试
      • @DavidRodríguez-dribeas:大卫,它确实如此,除非您将该功能标记为“虚拟”。例如,如果你有一些函数被标记为“final”并且没有虚拟基,你会得到一个错误,比如“只有虚拟成员函数可以被标记为“final””
      【解决方案4】:

      virtual 关键字在驱动类中是可选的,因为根据规则,当您使用具有虚函数的基类驱动类时,并且当您在驱动类编译器中重写虚函数时,会隐式分配 virtual 关键字以及函数。因此,您不需要显式分配 virtual 关键字。但是这个关键字在多级继承期间是必需的。

      例子:

      在您的代码中,我们添加此代码。

         class derived: public base {
          public:
              virtual void method() {    // In this line virtual keyword is optional.
                    std::cout << std::endl << "DERIVED :: method function" << std::endl;
              }
      
              virtual void display() {
                    std::cout << std::endl << "DERIVED :: display function" << std::endl;
              }
         };
      
         class deriveChild: public derived {
              public:
                  void method() {
                       std::cout << std::endl << "DERIVECHILD :: method" << std::endl;
                  }
      
              void display() {
                       std::cout << std::endl << "DERIVECHILD:: display" << std::endl;
                  }
         };
      

      在 main() 中,如果你使用下面的代码,它会给你不同的输出。

         base  *ptr = new deriveChild();
         ptr->method(); // will compile and execute
         ptr->display(); // will generate error because display() is not part of base class.
      

      现在如果你想使用deriveChild 类的display() 然后使用这个代码。

         derived *ptr = new deriveChild();
         ptr->method(); // Compile and Execute 
         ptr->display(); // Compile and Execute
      

      【讨论】:

        【解决方案5】:

        派生类中隐含的虚方法在派生类中是虚的,不需要显式定义为虚的。如果声明就是多余的声明。

        ptr->method();
        

        当编译器遇到上述语句时

        -> 它将尝试解析上述语句,因为 method() 函数是虚拟的,编译器将该调用的解析推迟到运行时。

        ->当你在运行时创建了派生类的对象,现在编译器会知道这个方法是派生类的。

        这个虚拟关键字在派生类中有什么额外作用?

        考虑这种情况,还有一个名为 Derived2 的派生类继承了派生形式,并且它有自己的虚方法。

         class derived2: public derived
        {
        public:
            virtual void method()
            {
                std::cout << std::endl << "DERIVED2" << std::endl;
            }
        };
        

        如果你像下面这样在 main 中调用 method()

        int main()
        {
            base* ptr = new derived2();
            ptr->method();   //derived2 class method() will get called
            return 9;
        }
        

        如果默认情况下derived2 中的method() 不是虚拟的,您最终将调用method() 的派生版本,失去运行时多态性的好处。

        因此,c++ 的作者在这里做了出色的工作,将虚拟关键字继承分层。

        【讨论】:

          猜你喜欢
          • 2012-12-01
          • 2018-04-22
          • 1970-01-01
          • 1970-01-01
          • 2013-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-28
          相关资源
          最近更新 更多