函数重写在c ++中

时间:2009-09-27 16:28:38

标签: c++

请有人解释c ++中的函数覆盖!也对虚拟功能概念感到困惑。一些教程说没有关键字virtual,派生类和基类对象都会调用基类函数。那覆盖是怎么发生的呢?

7 个答案:

答案 0 :(得分:3)

没有必要重新制作已经很好的东西。我认为thisthis是关于虚函数的精彩解释。那么您可能还需要了解abstract课程。如果您还有其他问题,请告诉我们。

答案 1 :(得分:1)

请阅读this。专注于C ++部分。然后在阅读后询问具体问题。

答案 2 :(得分:1)

让我尝试发布一个例子(这是我的想法,所以可能会有轻微的语法错误:))

基类:

class BaseClass
{
public:
   void normalFunction();
   virtual void virtualFunction();
}

派生类:

class DerivedClass : public BaseClass
{
public:
   void normalFunction();
   virtual void virtualFunction();
}

好的,所以我们定义了我们的类。现在,举一些例子:

void main()
{
    BaseClass base;
    DerivedClass derived;

    base.normalFunction();   //Invokes BaseClass::normalFunction();
    base.virtualFunction();  //Invoked BaseClass::virtualFunction();

    derived.normalFunction();//Invokes DerivedClass::normalFunction();
    derived.virtualFunction();//Invokes DerivedClass::virtualFunction();

    // Okay, nothing special yet, here comes the fun:
    BaseClass *basePtr = &base;
    BaseClass *derivedPtr = &derived;

    basePtr->normalFunction(); //Invokes BaseClass::normalFunction();
    basePtr->virtualFunction();//Invokes BaseClass::virtualFunction();

    derivedPtr->normalFunction(); //Invokes BaseClass::normalFunction(); !! this is because it's a BaseClass pointer.
    derivedPtr->virtualFunction();//Invokes DerivedClass::virtualFunction();
}

..总而言之,如果没有虚拟,指针的类型决定了将调用哪个方法,使用virtual,任何覆盖虚方法的类型都将调用它的方法,而不管指针类型如何:)

这是一个vtable(虚拟表)形式的非常小的开销,一个编译器细节,它将每个方法映射到不同的派生类型。

答案 3 :(得分:0)

每个至少有一个虚函数的C ++类都包含一个“虚拟表”或VTABLE,用于在运行时动态查找函数的地址。

假设您有两个类:Base和Derived。进一步假设Derived派生自Base,而“b”和“d”是Derived的实例,但b的编译时类型是Base,而d's是Derived。

现在假设Base和Derived都声明了一个函数“foo”。现在,如果在Base中声明“foo”是虚拟的,那么“foo”将在Base的VTABLE中有一个条目,当你调用“b.foo()”或“d.foo()”时,编译器会知道它是一个虚函数,它将注入代码,它将在运行时在VTABLE中查找“foo”的地址....这将找到类Derived中给出的“foo”定义的地址(即衍生:: foo),为

现在假设Base和Derived都声明了一个函数“foo”,但是“foo”还没有在Base中声明为虚拟。当你调用“b.foo()”或“d。”foo()时,编译器将尝试直接调用Base :: foo和Derived :: foo,绕过虚拟表查找。即使b的运行时类型可能是Derived ,b的编译时类型是Base,因此调用“b.foo()”将导致调用“Base :: foo”而不是“Derived :: foo”。

在C ++中,术语“覆盖”用于指代前一种情况;也就是说,当函数声明为virtual时,派生类中的另一个函数将替换基类中定义的原始函数。相比之下,“遮盖”一词用于指后一种情况;也就是说,派生类中的函数被调用而不是基类中的函数,只是因为它在范围上更接近,但它并没有真正替换基类中定义的函数。

答案 4 :(得分:0)

只有在超级虚拟中声明函数时才能执行覆盖。因为它是虚拟的所以它不是真的......这意味着有人用该签名调用它,它可能不会调用该函数。如果有人覆盖它,我们必须在运行时等待和查看。这就是虚拟的含义。

class A {
            void F_A() { cout << "A' A"; }
    virtual void F_B() { cout << "A' B"; }
}
class B : public A {
    void F_B() { cout << "B' B"; }
}

A o = new B();
o.F_A();  // Non-virtual so the compiler knows that it can only be the one in class A
o.F_B();  // Virtual so the compiler does not know if at runtime ... o is instance of A or B. So it have to wait and see.
// In this case, it's B at runtime ('`new B()`'), so it run the one in B.

总而言之,如果一个函数(更精确的方法)被声明为“virtual',它就可以被覆盖。否则,它不可能;因此,任何调用总是转到超类中的那个。

希望这有助于澄清。

答案 5 :(得分:0)

对于虚拟功能,请始终记住此拇指规则:

  
      
  • 当功能正常时功能   将调用对象类型,
  •   
  • 当功能为虚拟然后功能   实际实例的类型将是   调用。
  •   

答案 6 :(得分:0)

我喜欢用国际象棋棋盘来说明这个例子;

class ChessPiece
{
    public:
        void moveTo(Pos dst);
        virtual checkValidAndMoveTo(Pos dst) = 0;
};
class King: public ChessPieve
{        virtual checkValidAndMoveTo(Pos dst);}
class Queen: public ChessPieve
{        virtual checkValidAndMoveTo(Pos dst);}
// etc

ChessPiece*   board[8][8];

因此,当移动到正方形时,游戏将在棋子上调用checkValidAndMoveTo()。

board[pickedUpPiece.x][pickedUpPiece.y]->checkValidAndMoveTo()

现在将为特定部分调用相应的checkValidAndMoveTo()。一旦完成此移动,我们希望它调用MoveTo()。因为它知道它是什么类型,它将向下钻取并获得当前类型下方最多的MoveTo()版本。