【问题标题】:When is the "virtual" attribute actually needed in a derived class?派生类中何时真正需要“虚拟”属性?
【发布时间】:2014-01-24 17:47:20
【问题描述】:

我试图了解何时/何地应该编写“虚拟”关键字,以便给定类的任何后代启用多态行为,因为我无法在其他地方找到有关此问题的具体信息。

对于我所尝试的,在基类中的函数上使用 virtual 会自动使该函数对于任何后代都是虚拟的。然而互联网上的许多sn-ps经常在后代中再次声明这些功能是虚拟的。

struct A {
    virtual void foo1() { std::cout << "[A] foo1.\n"; }
    virtual void foo2() { std::cout << "[A] foo2.\n"; }
};
struct B : public A {
    virtual void foo1() { std::cout << "[B] foo1.\n"; }
            void foo2() { std::cout << "[B] foo2.\n"; }
};

用 g++ 编译这段代码,我发现 foo1 和 foo2 都具有多态行为。此外,即使我再次从 B 派生并创建一个 C 类,并且在没有 virtual 关键字的情况下再次实现 foo2,foo2 仍然使用 v-table。

在派生类中使用 virtual 关键字只是为了清晰和自我说明,还是有一些我不知道的其他影响?

【问题讨论】:

  • 派生类中的虚拟只是提供信息
  • 拥抱 C++11 override 关键字。它修复了由这种行为引起的许多令人讨厌的维护错误。

标签: c++ virtual


【解决方案1】:

For what I've tried, having virtual on a function in the base class automatically makes that function virtual for any descendants.

的确如此,子方法自动继承了父方法的virtualness。

人们在子类中重申 virtual 作为约定只是为了明确表明子类打算覆盖虚拟方法(尽管在 C++11 中有 override)。

【讨论】:

  • c++11 的最佳实践是什么?在子类中同时使用 virtual 和 override,还是直​​接覆盖?
  • 只需使用override。如果你搞砸了,编译器会告诉你 ;-)
【解决方案2】:

注意事项:

  1. 任何声明为虚拟的方法都是动态绑定的(运行时)。 任何其他方法都是静态绑定的(编译时)。

  2. 任何方法一旦声明为虚拟,对于下面的所有继承级别都保持虚拟,无论它是否在下面的类中声明为虚拟。即假设层次结构是这样的:Man->Indian->Tamilian.

如果 do_work() 是一个虚拟方法,那么即使对 Tamilian 来说它仍然是虚拟的,即使它仅在 Man 中被声明为虚拟。

虚拟关键字是如何工作的?

将 do_work() 声明为虚拟只是意味着:调用哪个 do_work() 将仅在运行时确定。

假设我这样做,

Man *man;
man = new Indian();
man->do_work(); // Indian's do work is only called.

如果不使用virtual,则由编译器静态确定或静态绑定,具体取决于调用的对象。因此,如果 Man 的对象调用 do_work(),则调用 Man 的 do_work() 即使它指向一个印度对象

令我惊讶的是,我发现纯虚函数也可以有一个实体!我以为他们不能。但是,事实是他们可以。这意味着可以调用抽象类的纯虚函数!!!两位非常优秀的作者是 Bjarne Stroustrup 和 Stan Lippman.... 因为他们编写了语言。

【讨论】:

    【解决方案3】:

    除非您打算派生它,否则子类应该在成员上使用 virtual 关键字并没有什么特别的原因。

    是否将您的类成员中的成员设为虚拟仅取决于您打算如何派生您的类。如果您不打算派生您的类,那么 no

    要获得更完整的解释,请考虑以下代码。如果我现在打算从 B 派生,或者使 B 成为可重用的一段代码,我可能会通过阻止访问 A 的成员来破坏代码。

       #include <iostream>
    
    class A
    {
    public:
      virtual void foo1()
      {
        std::cout<<"A.foo1"<<std::endl;
      }
      void foo2()
      {
        std::cout<<"A.foo2"<<std::endl;
      }
    };
    
    class B : public A
    {
    public:
      void foo1()
      {
        std::cout<<"B.foo1"<<std::endl;
      }
      void foo2()
      {
        std::cout<<"B.foo2"<<std::endl;
      }
    };
    
    
    int main()
    {
      A* a = new A();
      a->foo1();
      a->foo2();
      B* b = new B();
      b->foo1();
      b->foo2();
      delete a;
      a=b;
      a->foo1();
      a->foo2();
    }
    

    产生输出:

    A.foo1
    A.foo2
    B.foo1
    B.foo2
    B.foo1
    A.foo2
    

    【讨论】:

    • 您在此处显示的行为是由 A 函数上是否存在virtual 引起的。是否在 B 函数上指定 virtual 都没有区别。
    猜你喜欢
    • 1970-01-01
    • 2015-09-20
    • 2013-01-28
    • 2011-01-15
    • 2018-01-20
    • 2020-10-26
    • 2011-10-29
    • 2019-11-04
    • 1970-01-01
    相关资源
    最近更新 更多