C++中undefined reference to vtable和继承

34

文件 A.h

#ifndef A_H_
#define A_H_

class A {
public:
    virtual ~A();
    virtual void doWork();
};

#endif

文件 Child.h

#ifndef CHILD_H_
#define CHILD_H_

#include "A.h"

class Child: public A {
private:
    int x,y;
public:
    Child();
    ~Child();
    void doWork();
};
#endif

还有Child.cpp文件

#include "Child.h"

Child::Child(){
    x = 5;
}

Child::~Child(){...}

void Child::doWork(){...};
编译器报错:undefined reference to vtable for A。 我尝试了很多不同的方法,但都没成功。 我的目标是将类 A 定义为接口,并将实现代码与头文件分离。

4
你必须为每一个声明的非纯虚函数提供定义。对于声明但未使用的非虚函数,你无需提供定义。请注意保持原意不变,让内容更加通俗易懂。 - Lightness Races in Orbit
3个回答

77

为什么会出现这个错误以及如何解决?

您需要为class A中的所有虚函数提供定义。只有纯虚函数可以没有定义。

即,在class A中,这两个方法:

virtual ~A();
virtual void doWork();

应该被定义(应该有一个主体)

例如:

A.cpp

void A::doWork()
{
}
A::~A()
{
}

注意:
如果你希望你的A类充当接口(也就是C++中的抽象类),那么你应该将这个方法设置为纯虚函数。

virtual void doWork() = 0;

好文推荐:

什么是“虚表”未解析外部引用?
在构建C++程序时,链接器提示我的构造函数、析构函数或虚表未定义。


@user1227351:已更新答案以更好地解释。请阅读链接以获取进一步的说明。 - Alok Save
有 virtual A(); virtual void doWork() = 0; 仍然会出现 vtable 错误 =/。但是如果没有析构函数,它就可以正常工作。然而,如果没有析构函数,如果我做这样的事情:A *a = new Child(); delete a; 显然它不会调用 Child::Child()。有没有解决方法? - rjmarques
解决方案是将 A::~A(){} 放置在 Child.cpp 中。 - rjmarques
在C++11中,您可以使用“default”关键字让编译器完成工作。例如:virtual doSomething() = default; - Onur

6
我的目标是将A作为接口,并将实现代码与头文件分离。
在这种情况下,将A类中的成员函数声明为纯虚函数。
class A {
  // ...
  virtual void doWork() = 0;
};

如果我同时删除析构函数,那么就会消除这个错误。在这种情况下,如果我执行以下操作:A a = new Child(); delete a;它会调用哪个析构函数? - rjmarques
销毁顺序始终与构造顺序相反。在这种情况下,类A的析构函数必须是虚拟的,这强制执行先调用Child的析构函数,然后再调用A的析构函数。如果A类的析构函数不是虚拟的,则行为未定义。 - Mahesh
如果你将父类的析构函数声明为虚函数,那么在调用子类的析构函数后,会自动调用父类的析构函数。 - Izza
在A中同时拥有析构函数和纯虚函数doWork()会导致vtable错误。我该如何定义虚析构函数,以便A *a = new Child(); delete a; 调用Child::~Child()? - rjmarques
解决方案是在Child.cpp中放置A::~A(){}。 - rjmarques

2

如果其他回答都没有帮到你,确保删除任何“*.gch”文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接