在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
总共3种方法,一种是第四版书上的面向对象的教学方法。一种是实际中应该使用的简洁方法。一种是模板的方法。 1)第四版书中,面向对象的方法,基类,继承,多态 2)自己的更简洁的写法。(前提条件:如果不需要打印出表达式,仅仅计算结果) 3)自己的模板模拟多态的方法。
////////////////模板的方法///////////////////////////////////////////////////////////////// 第三种写法:利用模板模拟多态。 了解模板后,看到STL的迭代器的实现。发现模板也可以模拟多态。试了下,用模板写。感觉舒服多了。不用先写好基类和继承类。可以更自由的实现多态。 但模板会导致代码膨胀。比如 template<typename LT,typename RT> class AndQuery private: LT left; RT right; } 每次模板参数类型不一样。就会导致一个新的类的定义。(应该编译器会产生一些比如 xxxaaa_andquery 的class类。)应该是随着表达式的逻辑符号,增加一个就生成一个新类。 模板函数也是一样。根据参数类型的不一样。生成几份函数,就相当于重载。
#include <iostream> #include <vector> #include <set> using namespace std; class searchC { public: searchC(const vector<string>* p):content(p){} set<int> query(const string& key) { set<int> ret; int linesCount=content->size(); for(int i=0;i!=linesCount;++i) { string linec=(*content)[i]; if(linec.find(key)!=string::npos) { ret.insert(i+1); } } return ret; } private: const vector<string>* content; }; class Query{ public: Query(const string& _key); set<int> GetRnt(); static searchC sc; static int count_lines; private: Query(); string key; }; Query::Query(const string& _key):key(_key){} set<int> Query::GetRnt() { set<int> rnt= sc.query(key); return rnt; } //template////////////////////////////////////// template<typename LT,typename RT> class AndQuery { public: AndQuery(const LT& _left,const RT& _right):left(LT(_left)),right(RT(_right)){} set<int> GetRnt() { set<int> _ret; set<int> leftret=left.GetRnt(); set<int> rightret=right.GetRnt(); set<int>::const_iterator cit=leftret.begin(); for(cit;cit!=leftret.end();++cit) { if(rightret.find(*cit)!=rightret.end()) { _ret.insert(*cit); } } return _ret; } private: LT left; RT right; }; //template////////////////////////////////////// template<typename LT,typename RT> class OrQuery { public: OrQuery(const LT& _left,const RT& _right):left(LT(_left)),right(RT(_right)){} set<int> GetRnt() { set<int> leftret=left.GetRnt(); set<int> rightret=right.GetRnt(); leftret.insert(rightret.begin(),rightret.end()); return leftret; } private: LT left; RT right; }; template<typename LT> class NotQuery { public: NotQuery(const LT& _left):left(LT(_left)){} set<int> GetRnt() { set<int> _ret; set<int> leftret=left.GetRnt(); for(int i=1;i!=Query::count_lines+1;++i) { if(leftret.find(i)==leftret.end()) { _ret.insert(i); } } return _ret; return leftret; } private: LT left; }; template<typename T1,typename T2> AndQuery<T1,T2> operator&(const T1& _left,const T2& _right) { return AndQuery<T1,T2>(_left,_right); } template<typename T1,typename T2> OrQuery<T1,T2> operator|(const T1& _left,const T2& _right) { return OrQuery<T1,T2>(_left,_right); } template<typename T1> NotQuery<T1> operator!(const T1& _left) { return NotQuery<T1>(_left); } //main///////////////////// searchC Query::sc(0x0); int Query::count_lines=0; int main() { vector<string> context; context.push_back("little start."); context.push_back("a apple."); context.push_back("two apple."); context.push_back("dog"); context.push_back("two dog."); context.push_back("good dog"); searchC myQuery(&context); Query::sc=myQuery; Query::count_lines=context.size(); auto myQ=!(Query("dog")&Query("two")|Query("little")); set<int> rnt= myQ.GetRnt(); set<int>::iterator itrt=rnt.begin(); for(int i=0;i!=rnt.size();++i) { cout<<*(itrt++)<<endl; } return 0; }
××××××××××××××××××××仅仅计算结果的简单方法×××××××××××××××××××××××× 这是假定不需要打印出计算表达式的前提下。要打印的话,就必须树形结构保存数据之间的关系。
如果只需要计算机结果,发现原来有很简单的做法。不需要继承,不需要new和delete。并且表达式也是一样的简单:Query node= !(!Query("two")|Query("dog")&Query("good")); 继承的目的就是方法的多态。运行时才能知道到底调用的是那个方法。这是继承的魅力所在。 但是如果问题本身,可以不需要运行时去多态呢。编译时就把方法确定下来呢? 如这里。Query("two")就是生成一个node对象,对象包含一个成员变量,就是结果ret。 Query("dog")&Query("good") 也是生成一个node对象。构造对象的时候。立马把结果放入构造函数. 问题就立马解决了。 而不需要让Query("dog")&Query("good") 生成一个对象。对象又包含2个子对象的指针。要到调用求结果的方法时,才去调用子对象的求结果方法(多态)。 如果不是因为 操作符必须需要至少一个对象。连类都可以省去了。直接一个function方法就可以了。 如 set<int> ret=function("two")|function("dog"); 不过这里居于一个事实,就是临时对象。Query node= !(!Query("two")|Query("dog")&Query("good")); 不管你继不继承,这个表达式都会生成5个临时对象。编译器编译期间已经预留了栈内存给他们。应该赋值给左值node之后。就会调用它们的析构了。 所以用继承,多态,就必须用new,来保存对象的指针,从而到时候调用对象的方法。 而我们这里,每步已经求出没步的结果。所以不需要继承,不需要new和delete。 mian.cpp
searchC Query::sc(0x0);
textquery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include <set> #include <vector> using namespace std; class searchC { public: searchC(vector<string>* p):content(p){} set<int> query(const string& key) { set<int> ret; int linesCount=content->size(); for(int i=0;i!=linesCount;++i) { string linec=(*content)[i]; if(linec.find(key)!=string::npos) { ret.insert(i+1); } } return ret; } private: vector<string>* content; }; class Query{ public: Query(const string& _key); Query(const set<int>&); set<int> getRet(); static searchC sc; static int count_lines; private: Query(); set<int> ret; friend Query operator|(const Query& lht,const Query& rht); friend Query operator&(const Query& lht,const Query& rht); friend Query operator!(const Query& lht); }; Query operator&(const Query& lht,const Query& rht); Query::Query(const string& _key) { ret=sc.query(_key); } Query::Query(const set<int>& _ret):ret(_ret){} Query operator|(const Query& lht,const Query& rht) { set<int> _ret; _ret=lht.ret; _ret.insert(rht.ret.begin(),rht.ret.end()); return Query(_ret); } Query operator!(const Query& lht) { set<int> _ret; set<int> leftset=lht.ret; for(int i=1;i!=Query::count_lines+1;++i) { if(leftset.find(i)==leftset.end()) { _ret.insert(i); } } return Query(_ret); } Query operator&(const Query& lht,const Query& rht) { set<int> _ret; set<int> leftret=lht.ret; set<int> rightret=rht.ret; set<int>::const_iterator cit=leftret.begin(); for(cit;cit!=leftret.end();++cit) { if(rightret.find(*cit)!=rightret.end()) { _ret.insert(*cit); } } return Query(_ret); } set<int> Query::getRet() { return ret; } #endif // TEXTQUERY_H_INCLUDED
**********************************************************第一次的做,书中方法××××××××××××××××××××××××××××××××××××××××× c++ primer 的 textquery 例子,做了好几天。发现对入门c++基础是个很大检测,不像初看时,那么简单。 起码包含了几个知识点,智能指针,值类型智能指针,树的遍历(递归),构造和析构,多态,操作符重载。 1)编译器根据符号优先级(c++的文法句子),对方法的执行是一个树的遍历过程。所以我们最后我们得到的结果是一个树的根。如,Query tmp=~(Query(star)|Query(little)&Query(Twinkle)); tmp 是树的根节点。 2)用后续遍历法执行每个节点的eval方法。来模拟 编译器对表达式的计算思路。但仔细看的话,会知道方法的执行和当初节点的建立顺序其实不是一样的。不过不妨碍结果。 3)display的过程是一个树中序遍历的过程。
完整下载 ,ide:code block.
部分代码,方便快速查看。 textquery.h #ifndef TEXTQUERY_H_INCLUDED #define TEXTQUERY_H_INCLUDED #include "head.h" typedef vector<string>::size_type line_no; typedef map<string,set<line_no> >::iterator Type_mIt; typedef set<line_no>::iterator Type_sIt; class TextQuery { public: TextQuery(){} void readfile(ifstream &is) { store_file(is); build_map(); } set<line_no> run_query(const string& word) const { return word_map.find(word)->second; } string text_line(line_no line_n) const { if(line_n<=lines_of_text.size()) { return lines_of_text[line_n]; } else { throw string("out of range!"); } } map<string,set<line_no> >& getmap() { return word_map; } int getsumLine()const { return lines_of_text.size(); } set<line_no> getAllset()const { set<line_no> tmp; for(line_no i=0;i!=lines_of_text.size();++i) { tmp.insert(i); } return tmp; } ~TextQuery() { } private: //data vector<string> lines_of_text; map<string,set<line_no> > word_map; //text void store_file(ifstream& is) { string textLine; if (!is.is_open()) {cout << "Error opening file";} while (getline(is,textLine)) { lines_of_text.push_back(textLine); } } void build_map() { for(line_no i=0;i!= lines_of_text.size();i++) { istringstream words(lines_of_text[i]); string word; while(words >> word) { word_map[word].insert(i); word=""; } } } }; #endif // TEXTQUERY_H_INCLUDED
head.h #ifndef HEAD_H_INCLUDED #define HEAD_H_INCLUDED //标准库--》类型缩写--》类定义--》cpp文档,函数申明--》 通用函数。--》其他函数。 #include <set> #include <string> #include <vector> #include <map> #include <set> #include <iostream> #include <fstream> #include <cctype> #include <cstring> #include <sstream> #include <algorithm> using namespace std; typedef vector<string>::size_type line_no; //main.cpp void linkqureyf2(); void linkqureyf(); void simQuery(); void textquery(); void opqueryF(); void outLine(ostream& of,const string &content); void print_result(const map<string,set<line_no> >& wordsLine); void print_result2(const set<line_no> &wordsLine); void treefun(); //simple set<line_no> OrSet(const set<line_no>& ls,const set<line_no>& rs); set<line_no> AndSet(const set<line_no>& ls,const set<line_no>& rs); set<line_no> NotSet(const set<line_no>& sourceSet,const set<line_no>& AllSet); #endif // HEAD_H_INCLUDED
main.cpp #include "head.h" #include "textquery.h" #include "simplequery.h" #include "opquery.h" #include "LinkQuery.h" #include "lineLinkQuery.h" #include "TreeQuery.h" vector<u_p> LinkQuery::funs=vector<u_p>(); bool Query::debug=false; int main() { //textquery(); //simQuery(); //opqueryF(); //linkqureyf(); treefun(); return 0; } //依据表达式用最恰当的树的数据结构,来储存实例。建立树。 //采用后序遍历法,访问每个节点,执行方法,来达到表达式同样的结果(尽管顺序其实不是严格一致)。 void treefun() { ifstream infile("littlestar.txt"); TextQuery file; file.readfile(infile); outLine(cout,"words-lineNo mapping....................."); print_result(file.getmap()); string little="little"; string Twinkle="Twinkle"; string star="star"; Query tmp=~(Query(star)|Query(little)&Query(Twinkle)); cout<<tmp.NodeName()<<endl; cout<<tmp.display(cout)<<endl; print_result2(tmp.Eval(file)); } void textquery() { ifstream infile("littlestar.txt"); TextQuery file; file.readfile(infile); outLine(cout,"words-lineNo mapping....................."); print_result(file.getmap()); set<line_no> ret= file.run_query("little"); outLine(cout,"'little line-no:'....................."); print_result2(ret); } void outLine(ostream& of,const string &content) { of<<"***********"<<content<<"***********"<<endl; } void print_result(const map<string,set<line_no> >& wordsLine) { map<string,set<line_no> >::const_iterator beg1; for(beg1=wordsLine.begin();beg1!=wordsLine.end();beg1++) { cout<<beg1->first<<":"; set<line_no>::iterator bega=beg1->second.begin(); for(line_no i=0;i<beg1->second.size();i++,bega++) { cout<<*bega+1<<","; } cout<<endl; } } void print_result2(const set<line_no> &wordsLine) { set<vector<string>::size_type>::iterator beg=wordsLine.begin(); while(beg!=wordsLine.end()) { cout<<*beg+1<<","; ++beg; } cout<<endl; } treequery.h #ifndef TREEQUERY_H_INCLUDED #define TREEQUERY_H_INCLUDED #include "head.h" #include "textquery.h" //base class //所有方法,数据都private,让handle为友类访问,操作符重载方法也访问。 //注意看构造函数,//所以const 和&,需要认真理解,完成例子后,再用时间熟悉下const。 class Query; ostream& operator<<(ostream &os, const Query& q); Query operator&(const Query& lhs,const Query& rhs); Query operator|(const Query& lhs,const Query& rhs); Query operator~(const Query& lhs); class base_TNode { protected: virtual ~base_TNode(); private://方法全部隐藏,且虚函数。由handle class来代理,并根据实际对象,调用自己的。 virtual set<line_no> Eval(const TextQuery& _file)=0; virtual string NodeName()const=0;//因为此方法会被派生类的数据成员lhsrhs调用。 而Node_2L,和notnode中的数据成员lhs,rhs,都为const。所以这里要const。 virtual ostream& display(ostream& os)const=0;//同上。 friend class Query; friend Query operator&(const Query& lhs,const Query& rhs); friend Query operator|(const Query& lhs,const Query& rhs); friend Query operator~(const Query& lhs); }; base_TNode::~base_TNode(){} //handle class,有base_TNode,基类的指针成员。 //作为功能之一:智能指针,所有使用基类指针的地方,必须使用Query。以便计数器正确。 //作为功能之二,handle。handle class 必须代理派生类方法。 class Query { public: Query(const Query& up); Query& operator=(const Query& up); ~Query(); Query(const string& _word); set<line_no> Eval(const TextQuery& _file)const; string NodeName() const;//连派生类访问基类的方法。都要通过handle class。 ostream& display(ostream& os)const; //ostream & operator<<(const Query& _qy); static bool debug; private: Query(base_TNode* _p);//仅仅给操作符重载使用,隐藏,把操作符重载方法,加为友元。 base_TNode* p;//必须隐藏指针,基类的方法都由handle class。代理。 unsigned int * use; void del(); friend Query operator|(const Query& lhs,const Query& rhs); friend Query operator&(const Query& lhs,const Query& rhs); friend Query operator~(const Query& lhs); }; //leaf class Leaf:public base_TNode { private: Leaf(const string& _word);//隐藏派生类,只给handle class 友类来访问。 string NodeName()const; string word; set<line_no> Eval(const TextQuery&); ostream& display(ostream& os)const; friend class Query; }; //friend Query::Query(base_TNode* _p); Leaf::Leaf(const string& _word):word(_word){} string Leaf::NodeName()const { return word; } set<line_no> Leaf::Eval(const TextQuery& _file) { //if(no left child and no right child) //看成后序遍历的,递归终结的临界点。 return _file.run_query(word); } ostream& Leaf::display(ostream& os)const { return os<<word; } //base of 2-child node class Node_2L:public base_TNode { protected: Node_2L(Query _lhs, Query _rhs,const string & _opstr);//参数值传递Query,(handle class) 对象。这样构造的时候,会对lhs,rhs进行直copy。 //直copy会引发lhs,rhs中,自己的lhs,rhs直copy。一层一层。如此引用型计数器use,才能正常得到引用次数。临时对象析构时,才不会delete handle class,所指向的对象。 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论