C++沉思录第八章算数表达式树的面向对象问题的分析

刚开始看沉思录,觉得太枯燥。到了第八章,作者关于面向对象问题的分析,我follow书上的设计开发,理解了一些以前只是在书上看到的概念。

给自己做几点注解吧:

1.虚基类用来表达所有的继承类的共有特点,在这个例子中,所有的继承类都要有输出和求值计算,所以我们把这两个函数定义为虚函数。

2.虚基类必须至少含有一个纯虚函数。该纯虚函数可以定义也可以不定义。

3.我们要保证由虚基类派生出来的类的对象能被正确的析构,所以将虚基类的析构函数定义为虚函数。

4.对于虚函数如果没有定义,也应该使用一对{}来表明。

5.友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员),而且友元类应先定义在使用友元类的类的前面。

6.当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。

7.友元关系不可以被继承,也不可以传递,而且是单向的。

8.Expr的构造函数使用的参数为Expr,这样就可以嵌套构造Expr对象。

#ifndef EXPR_H
#define EXPR_H
#include<iostream>
#include<string>
using namespace std;

class Expr_node//抽象基类
{
	friend class Expr;
	int use;
	friend ostream & operator << (ostream &, const Expr_node &);
protected:
	Expr_node() :use(1){}
	virtual int eval()const=0;
	virtual ~Expr_node(){};
public:
	virtual void print(ostream &)const = 0;
};

class Expr//具体类
{
	friend class Expr_node;
	friend ostream& operator <<(ostream & o, const Expr &);
	Expr_node * p;
public:
	Expr(int);
	Expr(const string & op, Expr);
	Expr(const string &op, Expr, Expr);
	Expr(const string &op, Expr, Expr,Expr);
	Expr(const Expr &t){ p = t.p; ++p->use; }
	Expr& operator=(const Expr &);
	~Expr(){ if (--p->use == 0)delete p; }
	int eval()const { return p->eval(); }
};

class Int_node :public Expr_node
{
	friend class Expr;
	int n;
	Int_node(int k) :n(k){}
	void print(ostream & o)const{ o << n; }
	int eval()const{ return n; }
};

class Unary_node :public Expr_node
{
	friend class Expr;
	string op;
	Expr  opnd;
	Unary_node(const string & a, Expr  b) :op(a), opnd(b){}
	int eval()const;
	void print(ostream & o)const
	{	o << "(" << op << opnd << ")";	}
};

class Binary_node :public Expr_node
{
	friend class Expr;
	string op;
	Expr  left, right;
	Binary_node(const string & a, Expr  b, Expr  c)
		:op(a), left(b), right(c){}
	int eval()const;
	void print(ostream & o)const{ o << "(" << left << op << right << ")"; }
};

class Ternary_node :public Expr_node
{
	friend class Expr;
	string op;
	Expr left;
	Expr middle;
	Expr right;
	Ternary_node(const string & s, Expr l, Expr m, Expr r)
		:op(s), left(l), middle(m), right(r){}
	void print(ostream & o)const;
	int eval()const;
};

int Binary_node::eval()const
{
	int l = left.eval();
	int r = right.eval();

	if (op == "+")return l + r;
	if (op == "-")return l - r;
	if (op == "*")return l * r;
	if (op == "/"&&r!=0)return l / r;

	throw"error, bad op " + op + " int Binarynode";

}

int Unary_node::eval()const
{
	if (op == "-")return -opnd.eval();
	throw"error, bad op " + op + " int Unarynode";
}

Expr::Expr(int t)
{
	p = new Int_node(t);
}
Expr::Expr(const string & op, Expr e)
{
	p = new Unary_node(op, e);
}
Expr::Expr(const string &op, Expr d, Expr e)
{
	p = new Binary_node(op, d, e);
}
Expr::Expr(const string &op, Expr a, Expr b, Expr c)
{
	p = new Ternary_node(op, a, b, c);
}
Expr& Expr::operator=(const Expr &r)
{
	r.p->use++;
	if (--p->use == 0)delete p;
	p = r.p;
	return *this;
}

ostream & operator << (ostream &o, const Expr_node &t)
{
	t.print(o);
	return o;
}
ostream& operator <<(ostream & o, const Expr &r)
{
	r.p->print(o);
	return o;
}

void Ternary_node::print(ostream & o)const
{
	o << "( " << left << " ? "
		<< middle << " : " << right << " ) ";
}
int Ternary_node::eval()const
{
	if (left.eval())
		return middle.eval();
	else
		return right.eval();
}
#endif

#include<iostream>
#include"EXPR.h"
using namespace std;
int main()
{
	Expr t = Expr("*", Expr("-", 5), Expr("+", 3, 4));
	cout << t <<"="<<t.eval()<< endl;
	//t = Expr("*", t, t);
	//cout << t << "=" << t.eval() << endl;
	Expr m = Expr("?",-1, 99,26);
	cout << m << "=" << m.eval() << endl;
	return 0;
}
时间: 2024-08-07 22:25:48

C++沉思录第八章算数表达式树的面向对象问题的分析的相关文章

建算数表达式树并计算(应付作业,仅供参考)

只是为了应付数据结构老师布置的作业,算式里的数只能是整数: 例如输入 (6+3)*(4-2)*(41-1) 输出 720.00 #include <stdio.h> #include <string.h> const int N = 1000; int m[N], flag; double res; struct Node { double val; char c; Node* left; Node* right; Node() { left = right = NULL; c =

Trie树沉思录(1)

发现自己已经很久没有写解题报告了,很大一部分是因为懒,做完题之后不想再怎样了~不过最近发现写解题报告确实是有好处的,一方面可以复习,一方面可以梳理.还有就是可以给自己的岁月留下一点什么东西~今天是五一劳动节,就应该要劳动!我要重新着手写我的博客了~ 最近几个星期都在研究字符串,有点难,不过到现在为止Trie数学得还算是有那么点意思,写篇博文来记录一下! (关于Trie数是什么东西我就不想写了,我只写我个人的一些思考) Trie树通过共享前缀来达到了节约内存的目标,十分的强大!关于他的实现大概有两

匹配算数表达式语言

 The Definitive Antlr 4 Reference 2nd Edition  第4章第一小节 学习笔记 匹配算数表达式语言 本例中,只使用基本的算数运算(加,减,乘,除),括号表达式,整数,及变量.例如有如下的表达式. 193 a = 5 b = 6 a+b*2 (1+2)*3 在这里介绍的表达式语言,是由换行符所分割的一组语句构成.语句可以是表达式.赋值运算.或是一个空行.下面是用于解析上述语句及表达式的Antlr文法. grammar expr; prog : stat+;

&lt;&lt;C++ 沉思录&gt;&gt; 中文人民邮电出版 勘误

<<C++ 沉思录>> 中文人民邮电出版 勘误 这本中文版里面有各种坑爹的小错误. 比方说变量名的大小写, 同一个变量, 出现了大小写不一致, 等等问题都有. 然后今天感觉遇到了个语法问题. 关于继承权限的问题. 书中第八章的demo里面, 关于class Expr_node. 使用了protected关键字. 但是这里Expr_node是基类, 继承就会出现问题. 具体的代码如下: class Expr_node { friend ostream& operator &l

《C++沉思录》:类设计者的核查表——有关class的11问

本文的11个问题提取自<C++沉思录>第四章.所有问题的说明均为自己补充. 1 你的类需要一个构造函数吗? --正确的定义构造函数,把握好构造函数的职能范围 有些类太简单,它们的结构就是它们的接口,所以不需要构造函数. class print{ void print1(){cout<<"1"<<endl;} void print2(){cout<<"2"<<endl;} void print3(){cout

RxJava 沉思录(一):你认为 RxJava 真的好用吗?

本人两年前第一次接触 RxJava,和大多数初学者一样,看的第一篇 RxJava 入门文章是扔物线写的<给 Android 开发者的 RxJava 详解>,这篇文章流传之广,相信几乎所有学习 RxJava 的开发者都阅读过.尽管那篇文章定位读者是 RxJava 入门的初学者,但是阅读完之后还是觉得懵懵懂懂,总感觉依然不是很理解这个框架设计理念以及优势. 随后工作中有机会使用 RxJava 重构了项目的网络请求以及缓存层,期间陆陆续续又重构了数据访问层,以及项目中其他的一些功能模块,无一例外,我

C++沉思录之二——虚函数使用的时机

虚函数使用的时机 为什么虚函数不总是适用? 1. 虚函数有事会带来很大的消耗: 2. 虚函数不总是提供所需的行为: 3. 当我们不考虑继承当前类时,不必使用虚函数. 必须使用虚函数的情况: 1. 当你想删除一个表面上指向基类对象,实际却是指向派生类对象的指针,就需要虚析构函数. C++沉思录之二--虚函数使用的时机,布布扣,bubuko.com

C#中的表达式树的浅解

表达式树可以说是Linq的核心之一,为什么是Linq的核心之一呢?因为表达式树使得c#不再是仅仅能编译成IL,我们可以通过c#生成一个表达式树,将结果作为一个中间格式,在将其转换成目标平台上的本机语言.比如SQL.我们常用的Linq to sql就是这样生成SQL的. 表达式树是.NET 3.5之后引入的,它是一个强大灵活的工具(比如用在LINQ中构造动态查询). 先来看看Expression类的API接口: namespace System.Linq.Expressions { // // 摘

个性化的亲切——《沉思录》引发的感悟

记得初中那阵子,曾经追过明星,甚至美的标准也变成了他——恨不得所有的明星都是长的和他一样,唱的和他一样.除了他的歌,我几乎欣赏不了其他人的歌. 还记得差不多在那个年纪,曾经幻想过世界“大统”——我认为“大统”是达到“大同”,消弭纷争的有效方式.惭愧,后来知道希特勒也是这么想的.此乃后话,不提. 也几乎是那个时候,我不愿再做“出头鸟”,我相信“人多力量大”,我总愿意融在身边的“圈子”,不想显得自己不合群. 个性化,在我们的应试教育体制中从来都没有得到特别的提倡,如果没有良师益友的及时提点,一定会让