C++语言99个常见编程错误 常见错误14:捉摸不定的评估求值次序

常见错误14:捉摸不定的评估求值次序
  再没有比模糊的软件工程师设下的评估求值次序陷阱更能发现C++语言的C语言渊源印记了。
C语言和C++语言在表达式如何评估求值的问题上留下了很大的处理余地。这种灵活性能够使得
编译器生成高度优化的可执行代码,但同时也要求软件工程师更仔细地审视社稷这个问题的源代码,
以防止对评估求值次序作出任何无依据,先入为主的假设。
  子表达式的评估求值顺序不固定
  运算符的优先级和结合性对评估求值次序没有影响
  定位new的评估求值次序
  将评估求值次序固定下来的运算符

  不当的运算符重载 对于C++语言来说,运算符重载只是“语法糖”


  e.cpp
  #include "e.h"

E::E()
	{}

E::~E()
	{}

Plus::Plus( E *l, E *r )
	: l_(l), r_(r) {}

Plus::~Plus()
	{ delete r_; delete l_; }
	
// incorrect implementation below!
//int Plus::eval() const
//    { return l_->eval() + r_->eval(); }

// correct implementation
int
Plus::eval() const {
	int tmp = l_->eval();
	return tmp + r_->eval();
}

Times::Times( E *l, E *r )
	: l_(l), r_(r) {}

Times::~Times()
	{ delete r_; delete l_; }

int
Times::eval() const {
	int tmp = l_->eval();
	return tmp * r_->eval();
}

Int::Int( int value )
		: v_( value ) {}

int
Int::eval() const
	{ return v_; }

Var::Var( const std::string &id )
: id_( id ) {}

int
Var::eval() const
	{ return stab_[id_]; }

int
Var::set( int newValue )
	{ return stab_[id_] = newValue; }

std::map<std::string,int> Var::stab_;

Assign::Assign( Var *var, E *expr )
	: var_( var ), e_( expr ) {}

Assign::~Assign()
	{ delete e_; delete var_; }

int
Assign::eval() const
	{ return var_->set( e_->eval() ); }

Uminus::Uminus( E *e )
	: e_( e ) {}

Uminus::~Uminus()
	{ delete e_; }

int
Uminus::eval() const
	{ return -e_->eval(); }

e.h
#ifndef E_H
#define E_H

/*

// Grammar for constructing the hierarchy:
E -> E + E
E -> E * E
E -> int
E -> id
E -> id = E

*/

#include <string>
#include <map>

class E {
public:
	E();
	virtual ~E();
	virtual int eval() const = 0;
private:
	E( const E & );
	E &operator =( const E & );
};

class Plus : public E {
public:
	Plus( E *l, E *r );
	~Plus();
	int eval() const;
private:
	E *l_, *r_;
};

class Times : public E {
public:
	Times( E *l, E *r );
	~Times();
	int eval() const;
private:
	E *l_, *r_;
};

class Int : public E {
public:
	Int( int value );
	int eval() const;
private:
	int v_;
};

class Var : public E {
public:
	Var( const std::string &id );
	int eval() const;
	int set( int newValue );
private:
	std::string id_;
	static std::map<std::string,int> stab_;
};

class Assign : public E {
public:
	Assign( Var *var, E *expr );
	~Assign();
	int eval() const;
private:
	Var *var_;
	E *e_;
};

class Uminus : public E {
public:
	Uminus( E *e );
	~Uminus();
	int eval() const;
private:
	E *e_;
};

#endif

eparse.cpp
#include <cstring>
#include <cctype>
#include "e.h"
#include "eparse.h"

using namespace std;


/*
	Scanner:  return the next token in the input stream.
		attributes for integer constants and identifiers
		available until next invocation
*/
void
ExprLex::scan() {
	int c;
	while( true )
		switch( c = nextchar() ) {
		case '+': case '-':
		case '*': case '/':
		case '(': case ')':
		case '=':
			tok = c;
			return;
		case ' ': case '\t':
			continue;
		case '\n':
			tok = EOLN;
			return;
		default:
			if( isdigit( c ) ) {
				char *s = str;
				do
					*s++ = c;
				while( isdigit( c = nextchar() ) );
				*s = '\0';
				unnextchar( c );
				tok = INT;
				return;
			}
			if( isalpha( c ) ) {
				char *s = str;
				do
					*s++ = c;
				while( isalnum(c = nextchar()) );
				*s = '\0';
				unnextchar( c );
				tok = ID;
				return;
			}
			tok = BAD;
			return;
		}
}


E *
ExprParser::parse() {
	scan();
	E *ast = s();
	if( token() != EOLN )
		error();
	return ast;
}

void
ExprParser::error() {
	throw std::range_error( "syntax error" );
}

E *
ExprParser::s() {
	E *root = 0;
	if( token() != EOLN )
		root = e();
	return root;
}

E *
ExprParser::e() {
	E *root = t();
	while( true )
		switch( token() ) {
		case '+':
			scan();
			root = new Plus( root, t() );
			break;
		case '-':
			// uncomment out if allowing binary minus
			//scan();
			//root = new Minus( root, t() );
			error(); // comment out if allowing binary minus
			break;
		default:
			return root;
		}
}

E *
ExprParser::f() {
	E *root;
	switch( token() ) {
	case ID:
		root = new Var( lexeme() );
		scan();
		if( token() == '=' ) {
			scan();
			root = new Assign( static_cast<Var *>(root), e() );
		}
		return root;
	case INT:
		root = new Int( atoi( lexeme() ) );
		scan();
		return root;
	case '(':
		scan();
		root = e();
		if( token() != ')' )
			error();
		scan();
		return root;
	case '-':
		// uncomment out if allowing unary minus
		scan();
		return new Uminus( f() );
	default:
		error();
	}
	return 0;	// will never execute, but some compilers
				// complain if not there
}

E *
ExprParser::t() {
	E *root = f();
	while( true )
		switch( token() ) {
		case '*':
			scan();
			root = new Times( root, f() );
			break;
		case '/':
			// uncomment out if allowing binary /
			//scan();
			//root = new Div( root, f() );
			error(); // remove if parsing binary /
			break;
		default:
			return root;
		}
}

eparse.h
#ifndef EPARSE_H
#define EPARSE_H

#include "e.h"
#include <cstring>


class Lex {	// generic lexical analyser interface
  public:
	Lex( std::size_t lexeme_size = 81 )
		: str( new char[ lexeme_size ] ) {}
	virtual ~Lex()
		{ delete [] str; }
	virtual void scan() = 0;
	int token() const
		{ return tok; }
	const char *lexeme() const
		{ return str; }
  protected:
	int tok;
	char *str;
};

class CharLex : public Lex {	// reads char stream
  protected:
	// unless overridden, reads from stdin
	virtual int nextchar()
		{ return getchar(); }
	virtual void unnextchar( char c )
		{ ungetc( c, stdin ); }
};

class ExprLex : public CharLex {	// lex for exprs
  public:
	void scan();
};

enum { ID = 257, INT, EOLN, BAD };

class Parser {	// generic parser interface
  public:
	Parser( Lex *lp )
		: lex( lp ) {}
	virtual ~Parser()
		{ delete lex; }
	virtual E *parse() = 0;
  protected:
	void scan()
		{ lex->scan(); }
	int token() const
		{ return lex->token(); }
	const char *lexeme() const
		{ return lex->lexeme(); }
  private:
	Lex *lex;
};

/*
	Predictive parser for a simple expression grammar:

		S --> E eoln
		E --> T {(+|-)T}
		T --> F {(*|/)F}
		F --> id | int | ( E ) | ID = E | -F
*/
class ExprParser : public Parser {
  public :
	ExprParser( Lex *lp )
		: Parser( lp ) {}
	~ExprParser()
		{}
	E *parse();
  private:
	E *s();
	E *e();
	E *t();
	E *f();
	void error();
};

#endif

main.cpp
#include <iostream>
#include <memory>
#include "e.h"
#include "eparse.h"

using namespace std;

int
main() {
	Parser *parser = 0;
	try {
		parser = new ExprParser( new ExprLex );
		while( true ) {
			cout << "Enter an expression:  " << flush;
			auto_ptr<E> root( parser->parse() );
			if( !root.get() )
				break;
			cout << "Result: " << root->eval() << endl;
			getchar();
		}
	}
	catch( const exception &e ) {
		cout << "***" << e.what() << "***" << endl;
		getchar();
	}
	catch( ... ) {
		cout << "caught some exception" << endl;
		getchar();
	}
	delete parser;

	return 0;
}


输出  输入一个表达式出结果,要不然出异常,打印出异常
Enter an expression:  1+1
Result: 2


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值