请有人解释c ++中的函数覆盖!也对虚拟功能概念感到困惑。一些教程说没有关键字virtual,派生类和基类对象都会调用基类函数。那覆盖是怎么发生的呢?
答案 0 :(得分:3)
答案 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()版本。