c++ primer 的 textquery 例子。
总共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);
int Query::count_lines=0;
void main_textquery() { 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(); Query node= !(!Query("two")|Query("dog")&Query("good")); set<int> ret=node.getRet(); set<int>::const_iterator cit=ret.begin(); for(cit;cit!=ret.end();++cit) { cout<<*cit<<endl; } }
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,所指向的对象。 const Query lhs; const Query rhs; ostream& display(ostream& os)const; private: string NodeName()const; string opstr; }; Node_2L::Node_2L(Query _lhs, Query _rhs,const string & _opstr):lhs(_lhs),rhs(_rhs),opstr(_opstr){} string Node_2L::NodeName()const { return "("+lhs.NodeName()+opstr+rhs.NodeName()+")"; } ostream& Node_2L::display(ostream& os)const { //按照简单的下面方式得不到正确结果,会把lhs.display(os)当成一个地址打出来。 //用简单方法,更容易维护。比如用NodeName方法同样的效果。那就必须重新解释<<的含义。重载<<。 //return os<<"("<<lhs.display(os)<<opstr<<rhs.display(os)<<")"; return os << "(" << lhs << " " << opstr << " "<< rhs << ")"; } //and node class AndNode:public Node_2L { private: AndNode( Query _lhs, Query _rhs); set<line_no> Eval(const TextQuery& _file); friend Query operator&(const Query& lhs,const Query& rhs);//操作符重载需要访问。 }; AndNode::AndNode(Query _lhs, Query _rhs):Node_2L(_lhs,_rhs,"&"){} set<line_no> AndNode::Eval(const TextQuery& _file) { set<line_no> leftret=lhs.Eval(_file); set<line_no> rightret=rhs.Eval(_file); return AndSet(leftret,rightret); } //or node class OrNode:public Node_2L { private: OrNode( Query _lhs, Query _rhs); set<line_no> Eval(const TextQuery& _file); friend Query operator|(const Query& lhs,const Query& rhs);//操作符重载需要访问。 }; OrNode::OrNode( Query _lhs, Query _rhs):Node_2L(_lhs,_rhs,"|"){} set<line_no> OrNode::Eval(const TextQuery& _file) { set<line_no> leftret=lhs.Eval(_file); set<line_no> rightret=rhs.Eval(_file); return OrSet(leftret,rightret); } //not node class NotNode:public base_TNode { private: NotNode(Query lhs); set<line_no> Eval(const TextQuery& _file); ostream& display(ostream& os)const; string NodeName()const; const Query lhs; friend Query operator~(const Query& lhs); }; NotNode::NotNode(Query _lhs):lhs(_lhs){} string NotNode::NodeName()const { return "~("+lhs.NodeName()+")"; } set<line_no> NotNode::Eval(const TextQuery& _file) { set<line_no> leftret=lhs.Eval(_file); return NotSet(leftret,_file.getAllset()); } ostream& NotNode::display(ostream& os)const { ////??????????????????????? return os << "~(" << lhs << ")"; } //定义 Query::Query(base_TNode * _p):p(_p),use(new unsigned int(1)){if(Query::debug){cout<<p->NodeName()<<": '*p' constr to query:"<<p<<" use:."<<*use<<endl;}} Query::Query(const Query& up) { p=up.p; use=up.use; ++*use; if(Query::debug){cout<<p->NodeName()<<": ref:"<<p<<" use:."<<*use<<endl;} } Query& Query::operator=(const Query& up) { ++*up.use; del(); p=up.p; use=up.use; if(Query::debug){cout<<p->NodeName()<<": copy =:"<<p<<" use:."<<*use<<endl;} return *this; } Query::~Query() { string tmp=p->NodeName(); if(Query::debug){cout<<tmp<<": before delete1 :"<<p<<" use:."<<*use<<endl;} del(); } void Query::del() { string tmp=p->NodeName(); if(Query::debug){cout<<tmp<<": before delete2 :"<<p<<" use:."<<*use<<endl;} if(--*use==0) { delete p; delete use; if(Query::debug){cout<<tmp<<": delete end"<<endl;} } else { if(Query::debug){cout<<tmp<<": delete end(just sub use.) :"<<p<<" use:."<<*use<<endl;} } } string Query::NodeName()const { return p->NodeName(); } Query::Query(const string& _word) { p=new Leaf(_word); use=new unsigned int(1); if(Query::debug){cout<<p->NodeName()<<": query 'word' construct "<<p<<" use:."<<*use<<endl;} } set<line_no> Query::Eval(const TextQuery& _file)const { return p->Eval(_file); } ostream& Query::display(ostream& os)const { return p->display(os); } //end query 定义 Query operator&(const Query& lhs,const Query& rhs) { // base_TNode *p=new AndNode(lhs,rhs); // if(Query::debug){cout<<p->NodeName()<<": pointer create "<<p<<endl;} // Query tmp=Query(p); // // return tmp; return Query(new AndNode(lhs,rhs));//raii 原则,获得资源即初始。 } Query operator|(const Query& lhs,const Query& rhs) { // base_TNode *p=new OrNode(lhs,rhs); // if(Query::debug){cout<<p->NodeName()<<": pointer create "<<p<<endl;} // Query tmp=Query(p); // // return tmp; return Query(new OrNode(lhs,rhs));//raii 原则,获得资源即初始。 } Query operator~(const Query& lhs) { // base_TNode *p=new NotNode(lhs); // if(Query::debug){cout<<p->NodeName()<<": pointer create "<<p<<endl;} // Query tmp=Query(p); // // return tmp; return Query(new NotNode(lhs));//raii 原则,获得资源即初始。 } inline ostream& operator<<(ostream &os, const Query& q) { return q.display(os); } #endif // TREEQUERY_H_INCLUDED
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· Winform-耗时操作导致界面渲染滞后
· Phi小模型开发教程:C#使用本地模型Phi视觉模型分析图像,实现图片分类、搜索等功能
· 语音处理 开源项目 EchoSharp
· drools 规则引擎和 solon-flow 哪个好?solon-flow 简明教程