Передача объектов, которые являются подклассами абстрактного X, и создание экземпляров полей, которые имеют подкласс типа абстрактного X - PullRequest
0 голосов
/ 22 апреля 2020

У меня есть этот базовый класс

struct Expr
{
    virtual void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor) = 0;

    explicit Expr() = default;
    virtual ~Expr() = default;
};

И некоторые подклассы, например:

struct Binary : Expr
{
    explicit Binary(Expr &left, Token &_operator, Expr &right);
    Expr left;
    Token _operator;
    Expr right;

    void accept(Expr &expr, ExprVisitor &visitor) override;
};

struct Grouping : Expr
{
    explicit Grouping(Expr &expression);
    Expr expression;

    void accept(Expr &expr, ExprVisitor &visitor) override;
};

Теперь, поскольку Expr имеет чисто виртуальную функцию, невозможно иметь объект Expr, но я хочу , в качестве аргументов для двоичных и группирующих конструкторов, для них нужно взять другие двоичные выражения / выражения группировки / подкласса. Как мне это сделать?

Есть ли способ передать в качестве аргумента "любой подкласс X", я никогда не хочу, чтобы X был допустимым аргументом (на самом деле это невозможно сделать, поскольку это абстрактный класс )

Я думал об использовании умных указателей, кто-нибудь может привести пример?


РЕДАКТИРОВАТЬ

Итак, я попробовал реализации интеллектуальных указателей, но у меня возникают проблемы с взаимодействием других классов с интеллектуальными указателями:

// expr_impl.hpp
struct Binary : Expr
{
    explicit Binary(std::shared_ptr<const Expr> &left, Token &_operator, std::shared_ptr<const Expr> &right);
    std::shared_ptr<const Expr> left;
    Token _operator;
    std::shared_ptr<const Expr> right;

    void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor) override;
};

struct Grouping : Expr
{
    explicit Grouping(std::shared_ptr<const Expr> &expression);
    std::shared_ptr<const Expr> expression;

    void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor) override;
};

struct LiteralExpr : Expr
{
    explicit LiteralExpr(Literal &literal);
    const Literal literal;

    void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor);

};

// expr_impl.cpp

Binary::Binary(std::shared_ptr<const Expr> &left, Token &_operator, std::shared_ptr<const Expr> &right) : left(left), _operator(_operator), right(right) {}

void Binary::accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor)
{
    visitor.visit(*this);
}

Grouping::Grouping(std::shared_ptr<const Expr> &expression) : expression(expression) {}

void Grouping::accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor)
{
    visitor.visit(*this);
}

LiteralExpr::LiteralExpr(Literal &literal) : literal(literal) {}

void LiteralExpr::accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor)
{
    visitor.visit(*this);
}

// exprvisitor.hpp

struct ExprVisitor
{

    virtual void visit(Binary &expr) = 0;
    virtual void visit(Grouping &expr) = 0;
    virtual void visit(LiteralExpr &expr) = 0;
    virtual void visit(Unary &expr) = 0;


    explicit ExprVisitor() = default;
    virtual ~ExprVisitor() = default;
};

// astprinter.hpp

struct AstPrinter : ExprVisitor
{

    void visit(Binary &expr) override;
    void visit(Grouping &expr) override;
    void visit(LiteralExpr &expr) override;
    void visit(Unary &expr) override;

    std::string result;
    std::string getResult();
};


// astprinter.cpp
void AstPrinter::visit(Binary &expr)
{
    result += "(";
    result += expr._operator.lexeme;
    expr.accept(expr.left, *this);
    // expr.accept(expr.right, *this);
    result += ")";
}

void AstPrinter::visit(Grouping &expr)
{
}

void AstPrinter::visit(LiteralExpr &expr)
{
}

1 Ответ

1 голос
/ 22 апреля 2020

Вы можете использовать умные указатели, чтобы воспользоваться преимуществом динамического полиморфизма c. Сначала измените конструкторы так, чтобы они принимали умные указатели на базовый класс, и удалите переменные абстрактного типа:

#include<memory>

struct Binary : Expr
{
    //modify constructor to accept shared_ptrs
    Binary(std::shared_ptr<Expr> left, Token &_operator, std::shared_ptr<Expr> right);
    //class members includes shared_ptrs to base class
    std::shared_ptr<Expr> left;
    Token _operator;
    std::shared_ptr<Expr> right;

    //overriding functions MUST have the same argument list as the virtual function it overrides
    void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor) override;
};

struct Grouping : Expr
{
    //modify constructor to accept shared_ptr to base class
    explicit Grouping(std::shared_ptr<Expr> expression);
    //class member includes shared_ptr to base class
    std::shared_ptr<Expr> expression;

    //overriding functions MUST have the same argument list as the virtual function it overrides
    void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor) override;
};

Для использования вы должны динамически распределить все экземпляры Grouping и Binary в shared_ptrs с помощью std :: make_shared и приведение к параметру shared_ptrs-to-base:

//struct for example below
struct Unary : public Expr{
    Unary(Token _operator);

    Token operator;

    void accept(std::shared_ptr<const Expr> &expr, ExprVisitor &visitor) override;
};

Token incrementToken{"++"};
//Construct shared_ptr to Unary (on the right hand side), then up-cast to shared_ptr to Expr (on the left hand side)
std::shared_ptr<Expr> incrementExpression = std::make_shared<Unary>(incrementToken);

//Construct shared_ptr to Grouping (on the right hand side) using incrementExpression, then up-cast to shared_ptr to Expr (on the left hand side)
std::shared_ptr<Expr> exampleGrouping = std::make_shared<Grouping>(incrementExpression);

Token addToken{"+"};
//Construct shared_ptr to Binary (on the right hand side) using both incrementExpression and exampleGrouping, then up-cast to shared_ptr to Expr (on the left hand side)
std::shared_ptr<Expr> addExpression = std::make_shared<Binary>(incrementExpression, addToken, exampleGrouping);

Таким образом, вы никогда не создаете экземпляр переменной типа Expr, которую вы не можете, так как она является абстрактной. Однако экземпляры Binary и Grouping ведут себя так, как будто они являются Expr, и поэтому могут передаваться конструкторам для двоичного кода и группировки без разбора.

Используя интеллектуальные указатели, вы избегаете необходимости иметь дело непосредственно с памятью управление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...