据推测,链接器引用了对 vtable 的未定义引用

Linker refers to, supposedly, undefined reference to vtable

我正在尝试使用抽象 class 来表示子类型的公共基础。但是,无论我做什么,它(它似乎是链接器)都会抱怨 vtables 和未定义的引用。从错误消息来看,问题一定与析构函数有关。够奇怪的,它一直在谈论一个

"undefined reference to 'AbstractBase::~AbstractBase()'"

in child.cpp 这毫无意义。

和上次一样,我实际上不能展示我的代码,所以这里有一个本质上做同样事情的例子:

首先是摘要class,"AbstractBase.h":

#ifndef ABSTRACTBASE
#define ABSTRACTBASE

class AbstractBase
{
   public:
   virtual ~AbstractBase() = 0;
}
#endif

使用abstractbase的child,"child.h":

#ifndef CHILD
#define CHILD

class child : public AbstractBase
{
   public: 
      ~child() override;
}
#endif

"child.cpp"中的实现:

#include "child.h"
child::~child()

显然有更多的功能,但本质上这就是我真正的 class 的析构函数的样子。

在网上搜索了在 C++ 中使用抽象 classes 的方法后,我准备放弃了。据我从这些消息来源可以看出,这是做到这一点的方法。您将摘要 class 的析构函数声明为虚拟的,因此对它的任何调用都将包含 child。 child 的析构函数只是标记为覆盖。应该没有别的了。

我在这里错过了一些真正基本的东西吗?

PS: 添加了 MCVE:

class AbstractBase
{
   public:
   virtual ~AbstractBase() = 0;
};

class child : public AbstractBase
{
    public:
    void dostuff()
    {
      //stuff
    }

    ~child() override
    {}
}

int main (argc, char *argv[])
{
   child* ptr = new child();
   ptr->dostuff();
}

我要补充一点,我现在得到的错误并不完全相同,而原来的错误是这样的:

undefined reference to 'vtable for AbstractBase': In function AbstractBase:~AbstractBase()': Undefined reference to 'vtable for AbstractBase': Undefined reference to 'typeinfo for AbstractBase': Collect2:error:ld returned 1 exit status

你需要为每个class定义一个析构函数,否则你不能销毁那个class的对象(包括成员对象和基子对象)对象):

class AbstractBase
{
public:
   virtual ~AbstractBase() = default;
}; //                     ^^^^^^^^^^^

一些替代公式:

  • 用户自定义:

    struct AbstractBase {
        virtual ~AbstractBase() {}
    };
    
  • 纯虚拟,但定义:

    struct AbstractBase {
        virtual ~AbstractBase() = 0;
    };
    
    AbstractBase::~AbstractBase() = default;
    

    即使您没有其他虚拟成员函数,这样做的好处是可以保留 class 抽象。

  • 结合两者:

    struct AbstractBase {
        virtual ~AbstractBase() = 0;
    };
    
    AbstractBase::~AbstractBase() {}
    

感谢大家的帮助。我最终偶然发现了一个解决方案。

显然,在抽象 class 中使用常规虚函数会导致这些问题。我在我的 MCVE 中重新创建了修复和错误,观察:

无效代码:

class AbstractBase
{
public:
    virtual void idiot();
    virtual ~AbstractBase() = 0;
};

AbstractBase::~AbstractBase()=default;

class child : public AbstractBase
{
    public:
    void dostuff()
    {
        //stuff
    }
    void idiot() override
    {

    }

    ~child() override
    {

    }
};

int main(int argc, char *argv[])
{
   child* ptr = new child();
   ptr->dostuff();
}

功能代码:

class AbstractBase
{
public:
    //virtual void idiot();
    virtual ~AbstractBase() = 0;
};

AbstractBase::~AbstractBase()=default;

class child : public AbstractBase
{
    public:
    void dostuff()
    {
        //stuff
    }

    /*void idiot() override
    {

    }*/

    ~child() override
    {

    }
};

int main(int argc, char *argv[])
{
   child* ptr = new child();
   ptr->dostuff();
}

请注意我所做的 更改,正在注释掉虚函数白痴,并且它在 child 中实现。

在我看来,这是不合逻辑的。这个额外的功能应该不会造成问题。

或者,这是真正的解决方案,可以使所有虚函数成为纯函数。这解决了问题。

我只能猜测这里发生了什么,它似乎在 AbstractBase.cpp 中寻找非纯函数的实现,这当然不存在。结果是关于对所述 AbstractBase 的虚表和类型信息的未定义引用的讨论,声明虚函数确实未定义是正确的。但它不应该在意,class 是抽象的。

我的结论是,如果这是预期的功能,如果您要在 C++ 中使用抽象 classes,您确实需要将所有函数声明为纯函数,即使逻辑表明这是不必要的。无论如何,如果它确实是有意的,那么编译器应该警告用户。目前的错误信息完全没有用。