这篇博客主要学习的是C++项目组织或者说架构的一个小示例。优化的对象是以前博客中贴出来的用C++实现简易计算器的例子。需要说明的是,从上面提到的计算器小项目版本到现在正在看的计算器小项目版本,其中细节变化还是蛮多的,用术语来说就是代码优化多次或者迭代多次了,有些变量和函数都变了,所以想利用上面提到的版本以真正运行正在看的这版本代码还是有点的难的。具体可以去查看Bjarne Stroustrup的C++程序设计语言这书。在这里主要关注的是开发一个C++项目,它的整个项目结构会是怎么样的,读一个C++项目该从哪里开始?在这里展示一个小小的项目。
首先,在Bjarne Stroustrup的C++程序设计语言这书中提到,使用递归下降的语法分析算法实现。它将这个简易计算器分成五个部分,如下:
- 一个分析器,完成语法分析。
- 一个此法处理器,由字符组合出单词。
- 一个符号表,保存字符串和值的对偶。
- 一个驱动程序main()。
- 一个错误处理器。
下图是它们的依赖关系,箭头表示 使用 。大括号是列出了两个主要部分的具体内容。
其次,在具体实现部分,也分为五个部分。驱动文件main.cpp,头文件dc.h,分析器parser.cpp,词法处理器lexer.cpp和符号表table.cpp。由于该例子较小,在错误或者异常处理上负担不重,就没有单独划分为一个文件。图下为几个文件和标准库文件的依赖关系,箭头表示 使用 。
这里的.c文件和.cpp文件是一样的!
dc.h文件代码:
namespace Error{
struct Zero_divide{};
struct Syntax_error{
const char* p;
Syntax_error(const char* q)
{
p = q;
}
};
}
#include<string>
namespace Lexer{
enum Token_value{
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
extern Token_value curr_tok;
extern double number_value;
extern std::string string_value;
Token_value get_token();
}
namespace Parser{
double prim(bool get);
double term(bool get);
double expr(bool get);
using Lexer::get_token;
using Lexer::curr_tok;
}
#include<map>
extern std::map<std::string,double>table; //这里声明为全局变量是程序的一个瑕疵,书中也着重强调了这一点
namespace Driver{
extern int no_of_errors;
extern std::istream* input;
void skip();
}
parser.c文件代码:
#include"dc.h"
double Parser::prim(bool get)
{
/*...*/
}
double Parser::term(bool get)
{
/*...*/
}
double Parser::expr(bool get)
{
/*...*/
}
lexer.c文件代码:
#include"dc.h"
#include<iostream>
#include<cctype>
Lexer::Token_value Lexer::curr_tok;
double Lexer::number_value;
std::string Lexer::string_value;
Lexer::Token_value Lexer::get_token()
{
/*...*/
}
table.c文件代码:
#include"dc.h"
std::map<std::string,double>table;
main.c文件代码:
#include"dc.h"
#include<sstream>
int Driver::no_of_errors = 0;
std::istream* Driver::input = 0;
void Driver::skip()
{
/*...*/
}
int main(int argc,char* argv[])
{
/*...*/
}
总结,在这个示例中,首先对项目的功能进行划分,在实现的时候充分利用命名空间将各个划分的模块进行组合,使整个项目的组织架构比较清晰的呈现在头文件中。当然,在这个例子中也提到,也有不对的地方。使用了全局变量等。这也仅仅一个划分,书中还提到了多个头文件的划分。