19

我有三个不同的基类:

class BaseA
{
public:
    virtual int foo() = 0;
};

class BaseB
{
public:
    virtual int foo() { return 42; }
};

class BaseC
{
public:
    int foo() { return 42; }
};

然后我从这样的基础派生(用 X 代替 A、B 或 C):

class Child : public BaseX
{
public:
    int foo() { return 42; }
};

三个不同的基类中的函数是如何被覆盖的?我的以下三个假设是否正确?还有其他注意事项吗?

  • 使用 BaseA,子类不会编译,纯虚函数没有定义。
  • 使用 BaseB,当在 BaseB* 或 Child* 上调用 foo 时,将调用 child 中的函数。
  • 使用 BaseC,当在 Child* 上调用 foo 而不是在 BaseB* 上调用子函数时(调用父类中的函数)。
4

6 回答 6

16

要记住的重要规则是,一旦函数被声明为虚拟,派生类中具有匹配签名的函数始终是虚拟的。因此,它被 A 的子级和 B 的子级覆盖,它们的行为相同(除了您不能直接实例化 BaseA)。

但是,使用 C 时,该函数不会被覆盖,而是被重载。在这种情况下,只有静态类型很重要:它会根据它的指针(静态类型)而不是对象的真正含义(动态类型)来调用它

于 2009-06-22T15:10:00.087 回答
14

在派生类中,如果在基类中定义了虚拟方法,则该方法是虚拟的,即使在派生类的方法中未使用关键字 virtual。

  • 使用BaseA,它将按预期编译和执行,并且foo()是虚拟的并在类中执行Child
  • 与 相同BaseB,它也将按预期编译和执行,并且foo()是 virtual() 并在 class 中执行Child
  • BaseC但是,它将编译和执行,但是BaseC如果您从 的上下文中调用它,它将执行版本,如果您使用 的上下文调用,BaseC它将执行Child版本Child
于 2009-06-22T15:11:51.893 回答
2

使用 BaseA,子类不编译,纯虚函数未定义

仅当您尝试创建 BaseA 的对象时才适用。如果您创建 Child 的对象,然后您可以使用 BaseA* 或 Child* 调用 foo()

使用 BaseB,当在 BaseB* 或 Child* 上调用 foo 时,将调用 child 中的函数。

取决于对象的类型,因为对象可以是 BaseB 或 Child。如果对象是 BaseB,则调用 BaseB::foo。

使用 BaseC,当在 Child* 上调用 foo 而不是在 BaseB* 上调用 foo 时(调用父类中的函数)时,将调用子级中的函数。

是的,但你永远不想这样做。

于 2009-06-22T15:13:41.093 回答
2

从多态的角度来看,更喜欢 A,所以你知道每个孩子都有自己的虚函数实现。
如果您有一个有效的默认实现,主要选择 B,但是您必须确保所有子类都根据需要有自己的实现。C 不是多态性,因此请谨慎使用。

于 2009-06-22T15:23:09.657 回答
1

这主要取决于您如何称呼它。

如果你这样做了:

class Child : public BaseA
{
public:
    int foo() { return 42; }
};

并且做了

BaseA baseA = new Child();
baseA->foo();

它会调用 Child 的 foo 函数。

但是,如果您这样做:

BaseA baseA = new BaseA();

它会产生编译时错误。

于 2009-06-22T15:46:09.610 回答
0

如果从 A 派生类 Child编译,您只是不能实例化该类型的对象。

如果您要覆盖 Base 中的某些函数,然后再次派生,这可能很有价值。

于 2009-06-22T15:12:38.727 回答