何时在派生类中实际需要“虚拟”属性?

时间:2014-01-24 17:47:21

标签: c++ virtual

我正在尝试理解何时/何处应该编写'virtual'关键字,以便给定类的任何后代启用多态行为,因为我在其他地方找不到关于此问题的特定信息。

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

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。

在派生类中使用虚拟关键字仅用于清晰度和自我记录,还是有其他一些我不知道的效果?

3 个答案:

答案 0 :(得分:4)

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

这是真的,孩子会自动继承父方法的virtual

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

答案 1 :(得分:0)

注意事项:

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

  2. 任何声明为虚拟的方法,对于以下所有级别的继承都保持虚拟,无论它是否在下面的类中声明为虚拟。即假设等级是这样的:Man-&gt; Indian-&gt; Tamilian。

  3. 如果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 ....因为他们写了这种语言。

答案 2 :(得分:-1)

除非您打算派生它,否则没有特别的理由为什么子类应该在成员上拥有虚拟关键字。

是否仅使您的班级成员成为虚拟成员的决定取决于您打算如何推导您的班级。如果您不打算派生您的课程,那么

有关更完整的说明,请考虑以下代码。如果我现在打算派生出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