ChatGPT解决这个技术问题 Extra ChatGPT

是否所有虚函数都需要在派生类中实现?

这似乎是一个简单的问题,但我在其他任何地方都找不到答案。

假设我有以下内容:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Derived 类没有实现 bar() 函数可以吗?如果不是我的所有派生类都需要 bar() 函数怎么办,但有些需要。抽象基类的所有虚函数都需要在派生类中实现,还是只需要纯虚函数?谢谢


C
Community

派生类不必自己实现所有虚函数。他们只需要实现 pure 个。1 这意味着问题中的 Derived 类是正确的。它继承其祖先类 Abstractbar 实现。 (这假设 Abstract::bar 在某处实现。问题中的代码声明了方法,但没有定义它。您可以像 Trenki's answer 所示内联定义它,也可以单独定义它。)

即便如此,仅当派生类将被实例化时。如果派生类没有直接实例化,而只是作为更多派生类的基类存在,那么正是这些类负责实现其所有纯虚方法。层次结构中的“中间”类允许保留一些未实现的纯虚方法,就像基类一样。如果“中间”类确实实现了纯虚方法,那么它的后代将继承该实现,因此他们不必自己重新实现它。


甚至这个(纯虚函数的实现)只有在它们被实例化时(与作为抽象基类本身相反)。
这就是我所想的。但是我在我的项目中这样做,我收到一个链接错误,说 Derived::bar(); 有一个“未解析的外部符号”;但是我从来没有在 Derived 中声明 bar,那么为什么链接器要寻找函数体呢?
@pixelpusher 当然,Derived::bar 有一个函数体,即 Abstract::bar。因此,似乎定义的翻译单元(甚至在任何地方都定义了?)没有链接到调用它的翻译单元。
@Rob:They only need to implement the pure ones. 这是误导。派生类也不一定需要实现虚函数。
我感谢您的帮助,但@trenki 击中了头。虽然你也是正确的 Christian Rau,因为它没有被定义。
t
trenki

只有纯虚方法必须在派生类中实现,但您仍然需要其他虚方法的定义(而不仅仅是声明)。如果您不提供,链接器很可能会抱怨。

因此,只需将 {} 放在可选的虚拟方法之后即可为您提供一个空的默认实现:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

不过,更多涉及的默认实现将进入单独的源文件。


C
Community

ISO C++ 标准规定必须定义一个类的所有非纯虚方法。

简单地说,规则是:如果您的派生类覆盖了基类虚拟方法,那么它也应该提供一个定义,如果没有,那么基类应该提供该方法的定义。

根据您的代码示例中的上述规则,virtual void bar(); 需要在 Base 类中定义。

参考:

C++03 标准:10.3 虚函数 [class.virtual]

在类中声明的虚函数应在该类中定义或声明为纯(10.4),或两者兼而有之;但不需要诊断(3.2)。

因此,要么您应该使函数成为纯虚拟函数,要么为其提供定义。

gcc faq 也记录了它:

ISO C++ 标准规定必须定义一个类的所有非纯虚方法,但不需要对违反此规则 [class.virtual]/8 的任何诊断。基于此假设,GCC 将仅在定义其第一个此类非内联方法的翻译单元中发出隐式定义的构造函数、赋值运算符、析构函数和类的虚拟表。因此,如果您未能定义此特定方法,链接器可能会抱怨缺少明显不相关的符号的定义。不幸的是,为了改善此错误消息,可能需要更改链接器,但并非总是如此。解决方案是确保定义了所有不纯的虚拟方法。请注意,即使声明为纯虚拟 [class.dtor]/7,也必须定义析构函数。


J
Jason

是的,这很好......您只需要实现任何纯虚函数即可实例化从抽象基类派生的类。


C
CodeCodeCode

是的,派生类必须覆盖父类中纯虚拟的函数是正确的。具有纯虚函数的父类之所以称为抽象类,是因为它的子类必须给出自己的纯虚函数体。

对于普通虚函数: - 没有必要进一步覆盖它们,因为某些子类可能具有该功能,有些可能没有。

虚函数机制的主要目的是运行时多态性,纯虚函数(抽象类)的主要目的是否是强制要求与自己的主体同名的函数。