文章目录
1.表达式计算器设计:表达式树,类设计
-
表达式树
将5+3*6解析成表达式树,整个表达式的值就等于根节点+的值,而根节点的值=其左节点5+右节点*的值(根节点的计算要依赖于其左节点和右节点),而*节点的值=左节点3+右节点6的值。(逐层往下)
所以,要计算根节点的值,需要递归的遍历底下的子节点来进行计算。
-
类设计,继承体系
NumberNode数字节点的Calc方法等于数字本身,
BinaryNode二元运算节点:+或者*运算的节点,有两个操作数,左操作数,右操作数
加法运算节点AddNode和乘法运算节点MultiplyNode继承至BinaryNode;
使用startUML
Node是一个抽象类,其Calc方法是纯虚方法;
BinaryNode也是一个抽象类,不能实例化一个具体对象;
每个节点都有一个Calc方法,返回值是double;
抽象类需要在isAbstract处打勾,字体也变成了斜体;
最终得到的类图如下:
-
改进类继承体系:增加UnaryNode一元计算节点
与上面的相比,减号运算符是一元运算符,其孩子节点只有一个;
*,+运算符是二元运算节点,其孩子有2个;
UnaryNode是一元运算节点,它除了UMinusNode减号节点之外,还有函数调用FuncNode一元计算节点,即f(XXXX),不论XXX里面有多复杂的关系式,都把它看成是一元节点;
使用startUML
-
eg:P39\Node.h
#ifndef _NODE_H
#define _NODE_H
class Node
{
public:
//每个节点都有一个计算的纯虚函数
//类Node用于多态,派生类都要实现Calc
//Calc声明为const的,因为Calc不会改变类的成员
virtual double Calc() const = 0;
//类Node是多态类,析构函数也要声明为虚析构函数,否则基类指针指向派生类对象,
//通过基类指针释放对象的时候,是不会调用派生类的析构函数
virtual ~Node() {};
};
//NumerNode要实现这个纯虚函数Calc,为具体类;若没实现,还是抽象类
class NumberNode : public Node
{
public:
NumberNode(double number) : number_(number) {}
double Calc() const;
private:
const double number_;//加const的原因:因为number_初始化后就不会改变
};
//BinaryNode节点有2个子节点
//BinaryNode类没有实现Calc方法,BinaryNode类仍然是抽象类,只有它的派生类,加、减、乘、除节点才知道该如何计算
class BinaryNode : public Node
{
public:
BinaryNode(Node* left, Node* right)
: left_(left), right_(right) {}
~BinaryNode();//记得要释放left_和right_节点
protected:
Node* const left_;//const的作用:指针不能改变(即指针不能指向其它的节点),而不是指针所指向的内容不能改变
Node* const right_;
};
//于BinaryNode相比,它只有1个孩子
//UnaryNod也是抽象类,因为它没有实现Calc方法
class UnaryNode : public Node
{
public:
UnaryNode(Node* child)
: child_(child) {}
~UnaryNode();
protected:
Node* child_;
}
//加法运算节点AddNode
class AddNode : public BinaryNode
{
public:
//构造函数初始化,要调用基类部分的构造函数
AddNode(Node* left, Node* right)
: BinaryNode(left, right) {}
//要实现Calc方法,AddNode类是具体类
double Calc() const;
};
class SubNode : public BinaryNode
{
public:
SubNode(Node* left, Node* right)
: BinaryNode(left, right) {}
double Calc() const;
};
class MultiplyNode : public BinaryNode
{
public:
MultiplyNode(Node* left, Node* right)
: BinaryNode(left, right) {}
double Calc() const;
};
class DivideNode : public BinaryNode
{
public:
DivideNode(Node* left, Node* right)
: BinaryNode(left, right) {}
double Calc() const;
};
class UminusNode : public UnaryNode
{
public:
UminusNode(Node* child)
: UnaryNode(child) {}
double Calc() const;
};
#endif/* _NODE_H */
P39\Node.cpp
#include "Node.h"
#include <cmath.h>
#include <iostream>
//数字节点的计算方法就等于数字节点本身
double NumberNode::Calc() const
{
return number_;
}
BinaryNode::~BinaryNode()
{
delete left_;
delete right_;
}
UnaryNode::~UnaryNode();
{
delete child_;
}
double AddNode::Calc() const
{
//AddNode节点的值等于左计算节点得到的值+右计算节点得到的值
return left_->Calc() + right_->Calc();
}
double SubNode::Calc() const
{
return left_->Calc() - right_->Calc();
}
double MultiplyNode::Calc() const
{
return left_->Calc() * right_->Calc();
}
double AddNode::Calc() const
{
double divisor = right_->Calc();
if (divisor != 0.0)
return left_->Calc() / divisor;
else
{
std::cout << "Error: Divisor by zero" <<std::endl;
return HUGE_VAL;
}
}
double UnaryNode::Calc() const
{
//孩子节点前面加一个负号,对照改进类继承体系的图看更好理解
return - child_->Calc();
}
P39\main.cpp
#include <iostream>
int main(void)
{
return 0;
}