算术表达式树

一个面向对象范例(算术表达式树)

1.     问题描述

用程序来表示算术表达式。例如表达式(-5*3+4)对应的树如下:

该表达式树包括常数、一元运算符和二元运算符的节点。编写合适的函数来创建这样的树,然后打印该树的完整括号形式。

2.     分析

考虑定义一系列的类,用继承组织起来。这些类有一些共同的点:每个类都要存储一个值以及一些子节点。图中有三种节点:一种表示整数表达式,包含一个整数值,无子节点。另外两种分别表示一元表达式和二元表达式,包含一个操作符,分别有一个或两个子节点。各个节点之间是相互独立的。所以需要定义一个表示节点的基类。如下:

Expr.h

#ifndef   _EXPR_H

#define   _EXPR_H  

#include <iostream>

#include <string>

using namespace std;

namespace Meditation

{

    //公共基类,表示节点

 class Expr_node

 {

    friend ostream& operator<<(ostream &,const Expr_node &);

 public:

     virtual void print(ostream&) const =0;

     virtual ~Expr_node(){ }

 };

 

//整数表达式节点

 class Int_node:public Expr_node

 {

     friend class Expr;

     int n;

     Int_node(int k):n(k) {}

     void print(ostream & o) const

     {

        o<<n;

     }

 };

//一元表达式节点

 class Unary_node:public Expr_node

 {

     friend class Expr;

     string op;

     Expr_node * opnd;

     Unary_node(const string &a,Expr_node *b):op(a),opnd(b)

     { }

     void print(ostream &o)const

     {

        o<<"("<<op<<*opnd<<")";

     }

 

 };

 

 //二元表达式节点

 class Binary_node:public Expr_node

 {

     friend class Expr;

     string op;

     Expr_node *left;

     Expr_node *right;

     Binary_node(const string &a,Expr_node *b,

        Expr_node *c):op(a),left(b),right(c)

     { }

     void print(ostream &o)const

     {

        o<<"("<<*left<<op<<*right<<")";

     }

 };

}

#endif


 

Expr.cpp

#include "expr.h"

using namespace Meditation;

ostream& operator<<(ostream & o,const Expr_node & e)

{

    e.print(o);

    return o;

}


 

上面的方法,有一个问题。用户处理的不是值而是指针,所以必须记住分配和释放对象。动态分配:

Binary_node *t=new Binary_node(“*”,new Unary_node(“-”,new Int_node(5)),

                           new Binary_node(“+”,new new Int_node(3),

 new Int_node(4)));

 

这里,我们必须记住删除这些节点,但是我们不再拥有指向内层new调用所构造出来的对象的指针了!如果通过Binary_node和Unary_node的析构函数删除它们的操作数,也不行,因为可能会多次删除对象,可能有不止一个Expr_node指向同一个下层的表达式树对象。

3.     句柄类

在Expr_node类族中,仅仅表示了图中的圆圈,而没有对箭头建模,正是因为这里把箭头简单的描述成指针,这些用户需要亲自操作指针。所以,类Expr应该是一种句柄类,表示一个边或者说该树结构根源一个边。由于用户关心的是树而不是树种的单个节点,可以用Expr来隐藏Expr_node的继承层次。在Expr中通过构造函数创建三种Expr_node,并且将这个对象的地址存储在创建中的Expr对象中。Expr类的用户就不会直接看到Expr_node 对象了。

 

class Expr {

    friend class Expr_node;

    friend ostream& operator<<(ostream&, const Expr&);

    Expr_node* p;

public:

    Expr(int);

    Expr(const String&, Expr);

    Expr(const String&, Expr, Expr);

    Expr(const String&, Expr, Expr, Expr);       

    Expr(const Expr& t) { p = t.p; ++p->use; }//由于构造函数为Expr_node分配了内存,需要实现复制构造函数和赋值操作符管理下层的Expr_node

    ~Expr() { if (--p->use == 0) delete p; }

    Expr& operator=(const Expr& t);

    int eval() const { return p->eval(); }//新添加的,为了求表达式的值

};

Expr::Expr(int n)

{

        p = new Int_node(n);

}

 

Expr::Expr(const String& op, Expr t)

{

        p = new Unary_node(op, t);

}

 

Expr::Expr(const String& op, Expr left, Expr right)

{

        p = new Binary_node(op, left, right); //析构函数负责释放

}

 

Expr::Expr (const String& op, Expr left, Expr middle, Expr right)

{

    p = new Ternary_node(op, left, middle, right);

}


当Expr类复制一个Expr_node时,该Expr将其引用计数加1,当引用者为0,删除底层的Expr_node。

Expr&

Expr::operator=(const Expr& rhs) 

{

    rhs.p->use++;

    if (--p->use == 0) 

       delete p;

    p = rhs.p;

    return *this;

}

 

ostream&

operator<<(ostream& o, const Expr& t)

{

        t.p->print(o);

        return o;

}


 

避免复制:让每一个Expr_node包含一个引用计数器,指明同时有多少个Expr指向同一个Expr_node,Expr和Expr_node类协同管理引用计数,当且仅当一个Expr_node的引用计数等于0,该节点才被删除。所以需要在Expr_node中加入引用计数,Expr类声明为友元,帮助管理计数。

class Expr_node {

    friend class Expr;

        friend ostream& operator<<(ostream&, const Expr&);

    int use;

protected:

    Expr_node(): use(1) { }

        virtual void print(ostream&) const = 0;

        virtual int eval() const = 0;

        virtual ~Expr_node() { }

};


 

更改每个派生自Expr_node的类,令其操作为私有,将Expr声明为友元,存储Expr。

class Binary_node: public Expr_node {

    friend class Expr;

       string op;

        Expr left;

        Expr right;

        Binary_node(const string& a, Expr b, Expr c):

                op(a), left(b), right(c) { }

        void print(ostream& o) const

                { o << "(" << left << op << right << ")"; }

};


 

现在用户可以自由地声明Expr的对象和临时对象,构造任意复杂的表达式,而不需要考虑内存管理。

如:Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));

 

求表达式的值

cout << t << " = " << t.eval() << endl;

或者

t = Expr("*", t, t);

    cout << t << " = " << t.eval() << endl;

需要在Expr类中添加函数,把求值传递给其指向的Expr_node。

int eval() const { return p->eval(); }

在Expr_node中 添加一个虚函数。

class Expr_node {

protected:

        virtual int eval() const = 0;

       //其他和前面的相同

};

接着为每个派生自Expr_node的子类添加一个函数来实现求值运算。

Int_node最简单,直接返回其值即可。

int eval() const { return n; }  

对于Unbry_node

int

Unary_node::eval() const

{

    if (op == "-")

                return -opnd.eval();//会调用Expr类的成员eval,然后再调用其指向的Expr_node的虚函数eval,调用到了实际类型的eval();

        throw "error, bad op " + op + " in UnaryNode";

}


 

对Binary_node:

int

Binary_node::eval() const

{

        int op1 = left.eval();

        int op2 = right.eval();

 

        if (op == "-")  return op1 - op2;

        if (op == "+")  return op1 + op2;

        if (op == "*")  return op1 * op2;

        if (op == "/" && op2 != 0)  return op1 / op2;

 

        throw "error, bad op " + op + " in BinaryNode";

}


 

现在可以对表达式进行求值。

 

int main()

{

    Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));

    cout << t << " = " << t.eval() << endl;

    cout << "((-5)*(3+4)) = -35" << endl;

 

    t = Expr("*", t, t);

    cout << t << " = " << t.eval() << endl;

    cout << "(((-5)*(3+4))*((-5)*(3+4))) = 1225" << endl;

    return 0;

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值