我正在阅读一段代码,并惊讶于下面的代码(作为一个最小的例子)编译,并强迫要求在没有提供函数体的情况下覆盖函数是否合法?如果是,那么这是代码编写者对某些代码逻辑的有意设计吗?
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 ++的初级开发人员,我以其复杂性着称,我真的在问这个想法是否是一个公认的设计。
答案 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或更改优化级别可能(或可能不会)导致编译器发出对函数的引用,从而显示错误。