在没有提供定义的情况下覆盖函数有什么意义?

时间:2017-02-14 04:55:30

标签: c++ c++11

我正在阅读一段代码,并惊讶于下面的代码(作为一个最小的例子)编译,并强迫要求在没有提供函数体的情况下覆盖函数是否合法?如果是,那么这是代码编写者对某些代码逻辑的有意设计吗?

class Parent {
public:
    virtual void func1(){std::cout<< "Parent" << std::endl;};
};


class Child: public Parent {
public:
   void func1() override; // override, without function body
};

int main(){
  Child c;
  // c.func1(); // the code compiles is this line is commented.
}

回复评论

首先感谢Jerry Coffin的详细解释。

虽然我觉得有必要在主帖上写一些关于注释的内容,但我明白一般来说函数的定义是用cpp文件编写的,我确实在发布之前在cpp文件中查找了定义,我对这段代码的主要关注是,这段代码可能是故意以这种方式编写的,是的,虽然存在链接错误,但是这个链接错误可能是编码器打算暗示不应该调用此函数。作为C ++的初级开发人员,我以其复杂性着称,我真的在问这个想法是否是一个公认的设计。

2 个答案:

答案 0 :(得分:1)

如果对c::func()的呼叫处于活动状态(未注释掉),其余部分保持原样,则代码不会构建(至少在我见过的任何工具链中) 。具体来说,它编译得很好,但是当你进入链接阶段时,丢失的c::func1将导致一个未解决的外部错误。

这种情况发生的显而易见的方式是编辑中的意外,可能是在将代码拆分为标题和实现文件的过程中,或者可能是从标题和实现文件中整合代码(加上一些&#34;客户端& #34;实例化和使用类的代码)到一个文件中进行发布(或类似)。

在实际代码中,您通常希望在标头中看到类定义,并在匹配的源文件中看到实现,如下所示:

parent.h:

#ifndef PARENT_H_INC_
#define PARENT_H_INC_

class Parent {
public:
    virtual void func1();
};

#endif

child.h:

#ifndef CHILD_H_
#define CHILD_H_

#include "parent.h"

class Child: public Parent {
public:
   void func1() override; // override, without function body
};

#endif

parent.cpp:

#include "parent.h"

virtual void parent::func1() { 
    std::cout<< "Parent" << std::endl;
}

child.cpp:

#include "child.h"

void child::func1() override { 
    // Programmer has learned to prefer "\n" over std::endl;
    std::cout << "child\n"; 
}

main.cpp中:

#include "child.h"

int main() { 
    Child c;
    c.func1();
}

C ++的一个难点是需要手动拆分这样的代码,并保持多个文件的正确协调(尽管,公平地说,它确实也有一些优点)。

答案 1 :(得分:1)

编辑:发布的代码有点可构建(使用gcc,如果添加了#include并启用了优化),但下面的解释与 真实代码中的链接器错误。为了完整起见,我将它留在这里。 (结束编辑)

该程序看起来无效,为什么要编译?

这个问题有两个答案,一个是理论问题(基于语言标准)和一个实际问题(基于编译器和链接器通常如何工作)。

理论答案如下。该标准没有规定该计划的任何行为。必须定义任何声明的虚函数,但如果缺少定义,则不需要诊断(3.2 basic.def.odr&#34;一个定义规则&#34;)。所以一个实现可以自由地对这个程序做任何事情,例如,接受它就好像它没有任何问题。

实际答案说,所有对函数的引用都被优化掉了,所以不需要定义。它是类中的第一个虚函数,因此它控制发出该类的虚拟表的时间和位置。由于未定义,因此不会发出虚拟表,但由于未进行虚拟调用,因此永远不会使用vtable。链接器永远不会知道它曾经是需要的。添加和使用不同的虚拟fumction或更改优化级别可能(或可能不会)导致编译器发出对函数的引用,从而显示错误。