未定义参考'' vtable for class'

时间:2017-05-01 18:14:25

标签: c++ vtable visitor-pattern

我正在用C ++实现一个Visitor类,它为解析树生成XML输出。

当我在Windows上使用Clion进行编译时,代码会编译,但是当它在输出后运行时会发生崩溃。错误代码是这个

  

处理完成,退出代码为-1073741819(0xC0000005)

当我尝试使用gcc编译(没有Clion)时,我收到错误消息

  

未定义参考PrintXMLVisitor'的vtable。

我的代码如下。我把它提炼到产生错误的最小量

ASTNode.h

#ifndef MINILANG_ASTNODE_H
#define MINILANG_ASTNODE_H

#include <memory>
class Visitor;

class ASTNode {
public:
    virtual void accept(std::shared_ptr<Visitor> visitor) = 0;
};


#endif //MINILANG_ASTNODE_H

ASTTypeNode.h

#ifndef MINILANG_ASTTYPENODE_H
#define MINILANG_ASTTYPENODE_H


#include "ASTNode.h"

class ASTTypeNode: public ASTNode {
public:
    enum Type {Real, Int, Bool, String};
    ASTTypeNode(Type type);
    Type getType() const;

    void accept(std::shared_ptr<Visitor> visitor) override;

private:
    Type type;
};


#endif //MINILANG_ASTTYPENODE_H

ASTTypeNode.cpp

#include "ASTTypeNode.h"
#include "Visitor.h"

ASTTypeNode::ASTTypeNode(ASTTypeNode::Type type)
    : type(type)
{

}

ASTTypeNode::Type ASTTypeNode::getType() const {
    return type;
}

void ASTTypeNode::accept(std::shared_ptr<Visitor> visitor) {
    visitor->visit(std::shared_ptr<ASTTypeNode>(this));
}

Visitor.h

#ifndef MINILANG_VISITOR_H
#define MINILANG_VISITOR_H

#include <memory>
#include "ASTTypeNode.h"


class Visitor {
public:
    virtual void visit(std::shared_ptr<ASTTypeNode> typeNode) = 0;
};


#endif //MINILANG_VISITOR_H

PrintXMLVisitor.h

#ifndef MINILANG_PRINTXMLVISITOR_H
#define MINILANG_PRINTXMLVISITOR_H


#include "Visitor.h"

class PrintXMLVisitor: public Visitor {
public:
    void visit(std::shared_ptr<ASTTypeNode> typeNode) override;
};


#endif //MINILANG_PRINTXMLVISITOR_H

PrintXMLVisitor.cpp

#include "PrintXMLVisitor.h"
#include <iostream>

void PrintXMLVisitor::visit(std::shared_ptr<ASTTypeNode> typeNode) {

    std::string typeName;
    switch(typeNode->getType())
    {
        case ASTTypeNode::Type::Real:
            typeName = "Real";
            break;
        case ASTTypeNode::Type::Int:
            typeName = "Int";
            break;
        case ASTTypeNode::Type::Bool:
            typeName = "Bool";
            break;
        case ASTTypeNode::Type::String:
            typeName = "String";
            break;
        default:
            typeName = "Error";
            exit(22);
    }

    std::cout << "<TypeNode>" << typeName << "</TypeNode>" << std:: endl;
}

的main.cpp

#include <iostream>
#include "Lexer.h"
#include "ASTTypeNode.h"
#include "PrintXMLVisitor.h"

int main() {

    ASTTypeNode astTypeNode (ASTTypeNode::Type::Int);
    astTypeNode.accept(std::make_shared<PrintXMLVisitor>());


    return 0;
}

1 个答案:

答案 0 :(得分:-1)

制作一个不动态的共享指针。具体来说,

void ASTTypeNode::accept(std::shared_ptr<Visitor> visitor) {
    visitor->visit(std::shared_ptr<ASTTypeNode>(this)); // <=== HERE
}

该声明中的this指的是:

int main() 
{
    ASTTypeNode astTypeNode (ASTTypeNode::Type::Int); // <== this object
    astTypeNode.accept(std::make_shared<PrintXMLVisitor>());
    return 0;
}

更改工具链并不能解决这个问题你有选择,最明显的两个是:

  • 停止使用std::shared_ptr作为访问参数。
  • 使用标准库的ASTNodeType功能管理需要std::shared_ptr管理并从this共享的所有std:enable_shared_from_this个实例。

这些的前者是显而易见的(或者至少它是现在),所以我不会进一步讨论它。后者必然是微不足道的,因为它要求使用shared_from_this的基础类的任何实例必须由std::shared_ptr包装器管理。也就是说,目前没有像main()那样的具体结构。这可能会对您的整体代码库产生重大影响,因此请谨慎选择。

以上内容如何适用于您的案例:

首先,将ASTNodeType的派生链更改为:

class ASTTypeNode
    : public ASTNode
    , public std::enable_shared_from_this<ASTTypeNode> // ADDED

接下来,按如下方式使用shared_from_this

void ASTTypeNode::accept(std::shared_ptr<Visitor> visitor) 
{
    visitor->visit(shared_from_this()); // HERE
}

最后,尊重您已通过执行此操作来管理ASTNodeType实例共享的权证:

int main() 
{
    std::shared_ptr<ASTTypeNode> astTypeNode = std::make_shared<ASTTypeNode>(ASTTypeNode::Type::Int);
    astTypeNode->accept(std::make_shared<PrintXMLVisitor>());
    return 0;
}

那应该有用。在此处阅读有关上述代码中使用的内容的更多信息:

正如我所说,所有这些都是为了方便在只给出std::shared_ptr指针的对象中使用this。如果您可以首先删除该要求,那么它可能是一条更容易理解的路径,我会首先考虑这一点。