• 基于面向对象的表达式实现


    基于面向对象的表达式实现

             ——《C++沉思录》第8章 一个面向对象的程序范例

             本文我们介绍一个基于面向对象的表达式实现,并对其扩展。面向对象有三个基本要素:数据抽象(封装)、继承、动态绑定(多态)。这个程序可以很好的说明这三个特性,以及说明面向对象可以很好的使得程序可维护、更灵活、易扩展。

             我们给出以下表达式:

    (-5)*(3+4)

             其表达式树为:

           1.初步实现

             我们定义节点类,节点指向节点的边我们用指针表示。具体实现相见代码和注释。

    // 基于面向对象的表达式简单实现
    #include <iostream>
    #include <string>
    using namespace std;
    
    // 定义节点抽象基类
    class Expr_Node
    {
    protected:
        virtual void print(ostream&) const = 0;
    
    public:
        virtual ~Expr_Node() {}
    
        // 友元函数operator<<
        friend ostream& operator << (ostream&, const Expr_Node&);
    };
    
    ostream& operator << (ostream& o, const Expr_Node& e)
    {
        e.print(o);
        return o;
    }
    
    // 定义整数节点
    class Int_Node : public Expr_Node
    {
    private:
        int n;
    
    protected:
        void print(ostream&) const;
    
    public:
        Int_Node(int k);
    };
    
    void Int_Node::print(ostream& o) const
    {
        o << n;
    }
    
    Int_Node::Int_Node(int k) : n(k) {}
    
    // 定义一元操作符节点
    class Unary_Node : public Expr_Node
    {
    private:
        string     op;   // 操作符
        Expr_Node* opnd; // 操作数
    
    protected:
        void print(ostream&) const;
    
    public:
        Unary_Node(const string&, Expr_Node*);
        ~Unary_Node();
    };
    
    void Unary_Node::print(ostream& o) const
    {
        o << "(" << op << *opnd << ")";
    }
    
    Unary_Node::Unary_Node(const string& a, Expr_Node* b) : op(a), opnd(b) {}
    
    Unary_Node::~Unary_Node()
    {
        delete opnd;
    }
    
    // 定义二元操作符节点
    class Binary_Node : public Expr_Node
    {
    private:
        string op;
        Expr_Node* left;
        Expr_Node* right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Binary_Node(const string&, Expr_Node*, Expr_Node*);
        ~Binary_Node();
    };
    
    void Binary_Node::print(ostream& o) const
    {
        o << "(" << *left << op << *right << ")";
    }
    
    Binary_Node::Binary_Node(const string& a, Expr_Node* b, Expr_Node* c) : op(a), left(b), right(c) {}
    
    Binary_Node::~Binary_Node()
    {
        delete left;
        delete right;
    }
    
    int main()
    {
        Binary_Node* t = new Binary_Node("*",
                                         new Unary_Node("-", new Int_Node(5)),
                                         new Binary_Node("+", new Int_Node(3), new Int_Node(4)));
    
        cout << *t << endl;
    
        delete t;
        return 0;
    }

             以上实现中,我们只是对节点进行了建模,节点间的边用节点中的指针来表示。内存分配和回收需要用户手动管理。下面我们增添一个句柄类,用来自主管理内存分配和回收,这样可以省去用户管理内存的烦恼。

             用对象来管理内存的分配和回收,将内存管理的工作从用户那里剥离出来实现自动内存管理是面向对象编程的一大优势。

           2.增加句柄类——用对象管理内存

             我们增加一个句柄类,用其来代理实际的节点类。隐藏节点类,用户实际操作的是句柄类,同时句柄类完成了有关内存管理的工作。

             具体实现详见代码和注释。

    // 增加句柄类的实现,用对象管理内存
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Expr_Node;
    
    // 增加句柄类
    class Expr
    {
    private:
        Expr_Node* p;
    
    public:
        Expr(int); // 整数节点
        Expr(const string&, Expr); // 一元操作符节点
        Expr(const string&, Expr, Expr); // 二元操作符节点
        Expr(const Expr&); // 拷贝构造函数
        Expr& operator = (const Expr&); // 赋值运算符
        ~Expr(); // 析构函数
    
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    
    // 定义节点抽象基类
    class Expr_Node
    {
    private:
        int use; // 记录被指向个数,即节点的入度
    
    protected:
        virtual void print(ostream&) const = 0;
    
    public:
        Expr_Node() : use(1) {}
        virtual ~Expr_Node() {}
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
        // 友元函数operator<<
        //friend ostream& operator << (ostream&, const Expr_Node&);
        // 原来的重载operator<<注释,改为参数为cosnt Expr&的operator<<
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    // 原operator<<注释
    //ostream& operator << (ostream& o, const Expr_Node& e)
    //{
    //    e.print(o);
    //    return o;
    //}
    
    // 定义整数节点
    class Int_Node : public Expr_Node
    {
    private:
        int n;
    
    protected:
        void print(ostream&) const;
    
    public:
        Int_Node(int k);
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Int_Node::print(ostream& o) const
    {
        o << n;
    }
    
    Int_Node::Int_Node(int k) : n(k) {}
    
    // 定义一元操作符节点
    class Unary_Node : public Expr_Node
    {
    private:
        string     op;   // 操作符
        Expr       opnd; // 操作数
    
    protected:
        void print(ostream&) const;
    
    public:
        Unary_Node(const string&, Expr);
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Unary_Node::print(ostream& o) const
    {
        o << "(" << op << opnd << ")";
    }
    
    Unary_Node::Unary_Node(const string& a, Expr b) : op(a), opnd(b) {}
    
    // 定义二元操作符节点
    class Binary_Node : public Expr_Node
    {
    private:
        string op;
        Expr   left;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Binary_Node(const string&, Expr, Expr);
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Binary_Node::print(ostream& o) const
    {
        o << "(" << left << op << right << ")";
    }
    
    Binary_Node::Binary_Node(const string& a, Expr b, Expr c) : op(a), left(b), right(c) {}
    
    // Expr成员函数实现
    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 Expr& t)
    {
        p = t.p;
        ++p->use;
    }
    
    Expr::~Expr()
    {
        if (--p->use == 0)
        {
            delete p;
        }
    }
    
    Expr& Expr::operator = (const Expr& rhs)
    {
        ++rhs.p->use;
        if (--p->use == 0)
        {
            delete p;
        }
        p = rhs.p;
        return *this;
    }
    
    // 重载Expr的输出操作符<<
    ostream& operator << (ostream& o, const Expr& t)
    {
        t.p->print(o);
        return o;
    }
    
    int main()
    {
        Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));
    
        cout << t << endl;
    
        return 0;
    }

             实现中,需要修改Unary_Node和Binary_Node两个类的成员和构造函数,将原来的Expr_Node*指针改为Expr对象。

             增添一个句柄类用来代理原实际的节点类,句柄类自身完成内存管理的工作,使得客户端代码不必考虑底层的逻辑。

           3.扩展1——求值

             以上实现的功能是:建立表达式并将其输出。下面我们对表达式进行求值。实现原理是,在句柄类中添加一个求值函数,但这个求值函数并没有什么实现细节,只是对节点类求值函数的一次封装。实际的求值操作在节点类的成员函数中进行。

             具体细节详见代码和注释。

    // 扩展1——求值
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Expr_Node;
    
    // 增加句柄类
    class Expr
    {
    private:
        Expr_Node* p;
    
    public:
        Expr(int); // 整数节点
        Expr(const string&, Expr); // 一元操作符节点
        Expr(const string&, Expr, Expr); // 二元操作符节点
        Expr(const Expr&); // 拷贝构造函数
        Expr& operator = (const Expr&); // 赋值运算符
        ~Expr(); // 析构函数
    
        // 求值函数
        int eval() const;
    
    
        friend class Expr_Node;
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    
    // 定义节点抽象基类
    class Expr_Node
    {
    private:
        int use; // 记录被指向个数,即节点的入度
    
    protected:
        virtual void print(ostream&) const = 0;
    
    public:
        Expr_Node() : use(1) {}
        virtual ~Expr_Node() {}
    
        // 求值函数
        virtual int eval() const = 0;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
        // 友元函数operator<<
        //friend ostream& operator << (ostream&, const Expr_Node&);
        // 原来的重载operator<<注释,改为参数为cosnt Expr&的operator<<
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    // 原operator<<注释
    //ostream& operator << (ostream& o, const Expr_Node& e)
    //{
    //    e.print(o);
    //    return o;
    //}
    
    // 定义整数节点
    class Int_Node : public Expr_Node
    {
    private:
        int n;
    
    protected:
        void print(ostream&) const;
    
    public:
        Int_Node(int k);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Int_Node::print(ostream& o) const
    {
        o << n;
    }
    
    Int_Node::Int_Node(int k) : n(k) {}
    
    // 求值函数
    int Int_Node::eval() const
    {
        return n;
    }
    
    // 定义一元操作符节点
    class Unary_Node : public Expr_Node
    {
    private:
        string     op;   // 操作符
        Expr       opnd; // 操作数
    
    protected:
        void print(ostream&) const;
    
    public:
        Unary_Node(const string&, Expr);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Unary_Node::print(ostream& o) const
    {
        o << "(" << op << opnd << ")";
    }
    
    Unary_Node::Unary_Node(const string& a, Expr b) : op(a), opnd(b) {}
    
    // 求值函数
    int Unary_Node::eval() const
    {
        if (op == "-")
        {
            return opnd.eval();
        }
        throw "错误一元运算符!";
    }
    
    // 定义二元操作符节点
    class Binary_Node : public Expr_Node
    {
    private:
        string op;
        Expr   left;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Binary_Node(const string&, Expr, Expr);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Binary_Node::print(ostream& o) const
    {
        o << "(" << left << op << right << ")";
    }
    
    Binary_Node::Binary_Node(const string& a, Expr b, Expr c) : op(a), left(b), right(c) {}
    
    int Binary_Node::eval() const
    {
        int op1 = left.eval();
        int op2 = right.eval();
    
        if (op == "+")
        {
            return op1 + op2;
        }
        else if (op == "-")
        {
            return op1 - op2;
        }
        else if (op == "*")
        {
            return op1 * op2;
        }
        else if (op == "/")
        {
            if (op2 != 0)
            {
                return op1 / op2;
            }
            else
            {
                throw "除数为0!";
            }
        }
        else
        {
            throw "非法二元操作符!";
        }
    }
    
    // Expr成员函数实现
    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 Expr& t)
    {
        p = t.p;
        ++p->use;
    }
    
    Expr::~Expr()
    {
        if (--p->use == 0)
        {
            delete p;
        }
    }
    
    Expr& Expr::operator = (const Expr& rhs)
    {
        ++rhs.p->use;
        if (--p->use == 0)
        {
            delete p;
        }
        p = rhs.p;
        return *this;
    }
    
    // 求值函数
    int Expr::eval() const
    {
        return p->eval();
    }
    
    // 重载Expr的输出操作符<<
    ostream& operator << (ostream& o, const Expr& t)
    {
        t.p->print(o);
        return o;
    }
    
    int main()
    {
        Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));
    
        cout << t << " = " << t.eval() << endl;
    
        t = Expr("*", t, t);
    
        cout << t << " = " << t.eval() << endl;
    
        return 0;
    }

           4.扩展2——增加三元操作符

             增加一种新的操作符的方法是先从Expr_Node中派生一个新的节点类,在句柄类中添加相应的构造函数即可。

             具体细节将带代码和注释。

    // 扩展2——增加一个三元操作符
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Expr_Node;
    
    // 增加句柄类
    class Expr
    {
    private:
        Expr_Node* p;
    
    public:
        Expr(int); // 整数节点
        Expr(const string&, Expr); // 一元操作符节点
        Expr(const string&, Expr, Expr); // 二元操作符节点
        Expr(const string&, Expr, Expr, Expr); // 三元操作符节点
        Expr(const Expr&); // 拷贝构造函数
        Expr& operator = (const Expr&); // 赋值运算符
        ~Expr(); // 析构函数
    
        // 求值函数
        int eval() const;
    
    
        friend class Expr_Node;
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    
    // 定义节点抽象基类
    class Expr_Node
    {
    private:
        int use; // 记录被指向个数,即节点的入度
    
    protected:
        virtual void print(ostream&) const = 0;
    
    public:
        Expr_Node() : use(1) {}
        virtual ~Expr_Node() {}
    
        // 求值函数
        virtual int eval() const = 0;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
        // 友元函数operator<<
        //friend ostream& operator << (ostream&, const Expr_Node&);
        // 原来的重载operator<<注释,改为参数为cosnt Expr&的operator<<
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    // 原operator<<注释
    //ostream& operator << (ostream& o, const Expr_Node& e)
    //{
    //    e.print(o);
    //    return o;
    //}
    
    // 定义整数节点
    class Int_Node : public Expr_Node
    {
    private:
        int n;
    
    protected:
        void print(ostream&) const;
    
    public:
        Int_Node(int k);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Int_Node::print(ostream& o) const
    {
        o << n;
    }
    
    Int_Node::Int_Node(int k) : n(k) {}
    
    // 求值函数
    int Int_Node::eval() const
    {
        return n;
    }
    
    // 定义一元操作符节点
    class Unary_Node : public Expr_Node
    {
    private:
        string     op;   // 操作符
        Expr       opnd; // 操作数
    
    protected:
        void print(ostream&) const;
    
    public:
        Unary_Node(const string&, Expr);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Unary_Node::print(ostream& o) const
    {
        o << "(" << op << opnd << ")";
    }
    
    Unary_Node::Unary_Node(const string& a, Expr b) : op(a), opnd(b) {}
    
    // 求值函数
    int Unary_Node::eval() const
    {
        if (op == "-")
        {
            return opnd.eval();
        }
        throw "错误一元运算符!";
    }
    
    // 定义二元操作符节点
    class Binary_Node : public Expr_Node
    {
    private:
        string op;
        Expr   left;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Binary_Node(const string&, Expr, Expr);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Binary_Node::print(ostream& o) const
    {
        o << "(" << left << op << right << ")";
    }
    
    Binary_Node::Binary_Node(const string& a, Expr b, Expr c) : op(a), left(b), right(c) {}
    
    int Binary_Node::eval() const
    {
        int op1 = left.eval();
        int op2 = right.eval();
    
        if (op == "+")
        {
            return op1 + op2;
        }
        else if (op == "-")
        {
            return op1 - op2;
        }
        else if (op == "*")
        {
            return op1 * op2;
        }
        else if (op == "/")
        {
            if (op2 != 0)
            {
                return op1 / op2;
            }
            else
            {
                throw "除数为0!";
            }
        }
        else
        {
            throw "非法二元操作符!";
        }
    }
    
    // 扩展2——增加三元操作符 ?:
    class Ternary_Node : public Expr_Node
    {
    private:
        string op; // 操作符
        Expr   left;
        Expr   middle;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Ternary_Node(const string&, Expr, Expr, Expr);
        int eval() const;
    };
    
    void Ternary_Node::print(ostream& o) const
    {
        o << "(" << left << " ? " << middle << " : " << right << ")";
    }
    
    Ternary_Node::Ternary_Node(const string& a, Expr b, Expr c, Expr d) : op(a), left(b), middle(c), right(d) {}
    
    int Ternary_Node::eval() const
    {
        if (left.eval())
        {
            return middle.eval();
        }
        else
        {
            return right.eval();
        }
    }
    
    // Expr成员函数实现
    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(const Expr& t)
    {
        p = t.p;
        ++p->use;
    }
    
    Expr::~Expr()
    {
        if (--p->use == 0)
        {
            delete p;
        }
    }
    
    Expr& Expr::operator = (const Expr& rhs)
    {
        ++rhs.p->use;
        if (--p->use == 0)
        {
            delete p;
        }
        p = rhs.p;
        return *this;
    }
    
    // 求值函数
    int Expr::eval() const
    {
        return p->eval();
    }
    
    // 重载Expr的输出操作符<<
    ostream& operator << (ostream& o, const Expr& t)
    {
        t.p->print(o);
        return o;
    }
    
    int main()
    {
        Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));
    
        t = Expr("?:", t, 2, 1); // 测试三元操作符
    
        cout << t << " = " << t.eval() << endl;
    
        t = Expr("*", t, t);
    
        cout << t << " = " << t.eval() << endl;
    
        return 0;
    }

             该例很好的说明了面向对象的扩展性和灵活性。添加一种新的操作符,只需要从Expr_Node派生一个新的节点类,该类中添加具体的操作符和操作数,以及构造函数、打印函数、求值函数。在句柄类中,添加对应的新操作符的构造函数。在客户端代码中即可直接使用新操作符。

           5.扩展3——实现关系操作符功能

             关系操作符也是一种二元操作符,所以我们直接修改BinaryNode的eval函数即可。其他运算符比如位运算符、布尔运算符等等都可以继续添加。

             具体详见下面的代码。

           6.扩展4——添加赋值表达式

             赋值表达式首先需要添加一个表示变量的节点类,该类有一个名字和一个存储其值的空间。另外,还要添加一个赋值表达式类,用来进行实际的赋值操作。也就是说要想实现赋值表达式需要添加两个类:变量类和赋值类。

             实现细节详见代码和注释。

    // 扩展4——添加赋值表达式
    #include <iostream>
    #include <string>
    using namespace std;
    
    class Expr_Node;
    
    // 增加句柄类
    class Expr
    {
    private:
        Expr_Node* p;
    
    public:
        Expr(int); // 整数节点
        Expr(const string&, Expr); // 一元操作符节点
        Expr(const string&, Expr, Expr); // 二元操作符节点
        Expr(const string&, Expr, Expr, Expr); // 三元操作符节点
    
        Expr(const string&, int); // 变量节点
        Expr(const string&, Expr, Expr, int); // 赋值操作符节点
    
        Expr(const Expr&); // 拷贝构造函数
        Expr& operator = (const Expr&); // 赋值运算符
        ~Expr(); // 析构函数
    
        // 求值函数
        int eval() const;
    
    
        friend class Expr_Node;
        friend ostream& operator << (ostream&, const Expr&);
    
        friend class Assignment_Node;
    };
    
    
    // 定义节点抽象基类
    class Expr_Node
    {
    private:
        int use; // 记录被指向个数,即节点的入度
    
    protected:
        virtual void print(ostream&) const = 0;
    
    public:
        Expr_Node() : use(1) {}
        virtual ~Expr_Node() {}
    
        // 求值函数
        virtual int eval() const = 0;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
        // 友元函数operator<<
        //friend ostream& operator << (ostream&, const Expr_Node&);
        // 原来的重载operator<<注释,改为参数为cosnt Expr&的operator<<
        friend ostream& operator << (ostream&, const Expr&);
    };
    
    // 原operator<<注释
    //ostream& operator << (ostream& o, const Expr_Node& e)
    //{
    //    e.print(o);
    //    return o;
    //}
    
    // 定义整数节点
    class Int_Node : public Expr_Node
    {
    private:
        int n;
    
    protected:
        void print(ostream&) const;
    
    public:
        Int_Node(int k);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Int_Node::print(ostream& o) const
    {
        o << n;
    }
    
    Int_Node::Int_Node(int k) : n(k) {}
    
    // 求值函数
    int Int_Node::eval() const
    {
        return n;
    }
    
    // 定义一元操作符节点
    class Unary_Node : public Expr_Node
    {
    private:
        string     op;   // 操作符
        Expr       opnd; // 操作数
    
    protected:
        void print(ostream&) const;
    
    public:
        Unary_Node(const string&, Expr);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Unary_Node::print(ostream& o) const
    {
        o << "(" << op << opnd << ")";
    }
    
    Unary_Node::Unary_Node(const string& a, Expr b) : op(a), opnd(b) {}
    
    // 求值函数
    int Unary_Node::eval() const
    {
        if (op == "-")
        {
            return opnd.eval();
        }
        throw "错误一元运算符!";
    }
    
    // 定义二元操作符节点
    class Binary_Node : public Expr_Node
    {
    private:
        string op;
        Expr   left;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Binary_Node(const string&, Expr, Expr);
    
        // 求值函数
        int eval() const;
    
        // Expr的operator<<中调用protected的print函数,所以需要将Expr声明为friend
        friend class Expr;
    };
    
    void Binary_Node::print(ostream& o) const
    {
        o << "(" << left << op << right << ")";
    }
    
    Binary_Node::Binary_Node(const string& a, Expr b, Expr c) : op(a), left(b), right(c) {}
    
    int Binary_Node::eval() const
    {
        int op1 = left.eval();
        int op2 = right.eval();
    
        if (op == "+")
        {
            return op1 + op2;
        }
        else if (op == "-")
        {
            return op1 - op2;
        }
        else if (op == "*")
        {
            return op1 * op2;
        }
        else if (op == "/")
        {
            if (op2 != 0)
            {
                return op1 / op2;
            }
            else
            {
                throw "除数为0!";
            }
        }
        // 增加关系运算符
        else if (op == "<")
        {
            if (op1 < op2)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        else if (op == "<=")
        {
            if (op1 <= op2)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        else if (op == "==")
        {
            if (op1 == op2)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        else if (op == "!=")
        {
            if (op1 != op2)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        else if (op == ">=")
        {
            if (op1 >= op2)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        else if (op == ">")
        {
            if (op1 > op2)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
        // 可以继续添加其他二元操作符比如位运算符、布尔运算符等等
        // ……
        else
        {
            throw "非法二元操作符!";
        }
    }
    
    // 扩展2——增加三元操作符 ?:
    class Ternary_Node : public Expr_Node
    {
    private:
        string op; // 操作符
        Expr   left;
        Expr   middle;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Ternary_Node(const string&, Expr, Expr, Expr);
        int eval() const;
    };
    
    void Ternary_Node::print(ostream& o) const
    {
        o << "(" << left << " ? " << middle << " : " << right << ")";
    }
    
    Ternary_Node::Ternary_Node(const string& a, Expr b, Expr c, Expr d) : op(a), left(b), middle(c), right(d) {}
    
    int Ternary_Node::eval() const
    {
        if (left.eval())
        {
            return middle.eval();
        }
        else
        {
            return right.eval();
        }
    }
    
    // 变量类
    class Variable_Node : public Expr_Node
    {
    private:
        string name;
        int    value;
    
    protected:
        void print(ostream&) const;
    
    public:
        Variable_Node(const string&, int);
        int eval() const;
    
        friend class Assignment_Node;
    };
    
    void Variable_Node::print(ostream& o) const
    {
        o << name << "(" << value << ")";
    }
    
    Variable_Node::Variable_Node(const string& a, int b) : name(a), value(b) {}
    
    int Variable_Node::eval() const
    {
        return value;
    }
    
    // 赋值表达式节点
    class Assignment_Node : public Expr_Node
    {
    private:
        string op;
        Expr   left;
        Expr   right;
    
    protected:
        void print(ostream&) const;
    
    public:
        Assignment_Node(const string&, Expr, Expr);
        int eval() const;
    };
    
    void Assignment_Node::print(ostream& o) const
    {
        o << "(" << left << op << right << ")";
    }
    
    Assignment_Node::Assignment_Node(const string& a, Expr b, Expr c) : op(a), left(b), right(c) {}
    
    int Assignment_Node::eval() const
    {
        Variable_Node* v = dynamic_cast<Variable_Node*>(left.p);
        v->value = right.eval();
        return right.eval();
    }
    
    // Expr成员函数实现
    // 整数节点
    Expr::Expr(int k)
    {
        p = new Int_Node(k);
    }
    
    // 一元操作符
    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(const string& op, int k)
    {
        p = new Variable_Node(op, k);
    }
    
    // 赋值操作符
    Expr::Expr(const string& op, Expr left, Expr right, int x)
    {
        p = new Assignment_Node(op, left, right);
    }
    
    Expr::Expr(const Expr& t)
    {
        p = t.p;
        ++p->use;
    }
    
    Expr::~Expr()
    {
        if (--p->use == 0)
        {
            delete p;
        }
    }
    
    Expr& Expr::operator = (const Expr& rhs)
    {
        ++rhs.p->use;
        if (--p->use == 0)
        {
            delete p;
        }
        p = rhs.p;
        return *this;
    }
    
    // 求值函数
    int Expr::eval() const
    {
        return p->eval();
    }
    
    // 重载Expr的输出操作符<<
    ostream& operator << (ostream& o, const Expr& t)
    {
        t.p->print(o);
        return o;
    }
    
    int main()
    {
        //Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));
    
        //t = Expr("?:", t, 2, 1); // 测试三元操作符
    
        //cout << t << " = " << t.eval() << endl;
    
        //t = Expr("*", t, t);
    
        //cout << t << " = " << t.eval() << endl;
    
        Expr v = Expr("abc", 0);
    
        Expr t = Expr("=", v, Expr("+", 3, 4), 0);
        cout << t << endl;
    
        cout << t << " = " << t.eval() << endl;
    
    
        return 0;
    }

             对于赋值操作符的实现包括两个方面:变量的表示和对变量的赋值,以上的实现并不好,用到了强制类型转换。下一步将学习有关赋值运算符实现相关的原理和技巧。

           7.总结

             本文我们介绍了基于面向对象实现表达式,面向对象具有数据抽象、继承、动态绑定等特性,通过对表达式的实现,我们可以看出基于面向对象的程序具有很好的灵活性易于扩展。

             本文主要包含以下几部分:

             ·表达式的简单实现,主要是对节点建模,边用节点中的指针表示

             ·增加句柄类,对内部逻辑进行封装,使客户端代码清晰简单

             ·扩展1:对表达式进行求值

             ·扩展2:增加三元操作符

             ·扩展3:增加关系操作符,实质上还是二元操作符

             ·扩展4:添加赋值运算符,对于赋值运算符包括两部分:变量和对变量进行赋值

             表达式程序的实现进一步扩展可以实现一个简单的脚本语言。有关脚本语言编译原理相关内容我们会后续涉及。

  • 相关阅读:
    混用Int与IntPtr导致GetProcAddress始终返回null
    Net中获取程序集路径
    Sql server 2014 同一数据库换名还原,导致同名库一直处于还原状态
    微耕N3000注入
    Xaramin IOS 开发常见问题
    Vs2017 xaramin mac build agent部署后记
    Git 笔记
    spring AOP
    JAVA 反射原理
    Hyperledger Fabric:fabric private data技术【官方文档翻译】
  • 原文地址:https://www.cnblogs.com/unixfy/p/3462015.html
Copyright © 2020-2023  润新知