In order to better understand inheritance and polymorphism, make a small example of text query.
Interface class: Query has two methods.
- eval: query, return query result class QueryResult
- rep: get the text to query
How to use the client program:
//Query the row containing the text of Daddy Query q("Daddy"); //Query the row of text without Alice Query q = ~Query("Alice"); //Query, line with fiery or bird Query q = Query("fiery") | Query("bird"); //Query, with lines of fiery, bird or wind Query q = Query("fiery") & Query("bird") | Query("wind");
Interface class: Query, which has a private smart pointer of the parent class Query base.
The parent query base has children WordQuery, NotQuery and BinaryQuery (AndQuery, OrQuery).
Subclass is responsible for processing individual query results. For example, do And processing Or processing.
Query.h
#ifndef __QUERY_H__ #define __QUERY_H__ #include <string> #include <memory> #include <iostream> #include "TextQuery.h" class QueryResult; class Query; class Query_base{ friend class Query; protected: using line_no = TextQuery::line_no; virtual ~Query_base() = default; private: virtual QueryResult eval(const TextQuery&) const = 0; virtual std::string rep() const = 0; }; class Query{ friend Query operator~(const Query&);//Need to access private constructor friend Query operator|(const Query&, const Query&);//Need to access private constructor friend Query operator&(const Query&, const Query&);//Need to access private constructor public: Query(const std::string&);//Build a new WordQuery // Interface function: call the corresponding query base operation QueryResult eval(const TextQuery& t) const{ return q->eval(t); } std::string rep()const{ return q->rep(); } private: Query(std::shared_ptr<Query_base> query) :q(query){ std::cout << "Query pri:" << std::endl; } std::shared_ptr<Query_base> q; }; class WordQuery : public Query_base{ friend class Query;//Query uses WordQuery's private constructor WordQuery(const std::string& s) : query_word(s){ std::cout << "WordQuery:" << s << std::endl; } QueryResult eval(const TextQuery& t)const{ return t.query(query_word); } std::string rep()const{ return query_word; } std::string query_word; }; class NotQuery : public Query_base{ friend Query operator~(const Query&); NotQuery(const Query& q) :query(q){ std::cout << "NotQuery" << std::endl; } std::string rep() const { return "~(" + query.rep() + ")"; } QueryResult eval(const TextQuery&)const; Query query; }; inline Query operator~(const Query& op){ //return std::shared_ptr<Query_base>(new NotQuery(op)); std::shared_ptr<Query_base> tmp(new NotQuery(op)); return Query(tmp); } class BinaryQuery : public Query_base{ protected: BinaryQuery(const Query& l, const Query& r, std::string s) : lhs(l), rhs(r), opSym(s){ std::cout << "BinaryQuery" << std::endl; } std::string rep() const { return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } Query lhs, rhs; std::string opSym; }; class AndQuery : public BinaryQuery{ friend Query operator&(const Query&, const Query&); AndQuery(const Query& l, const Query& r) : BinaryQuery(l, r, "&"){ std::cout << "AndQuery" << std::endl; } QueryResult eval(const TextQuery&) const; }; inline Query operator&(const Query& lhs, const Query& rhs){ return std::shared_ptr<Query_base>(new AndQuery(lhs, rhs)); } class OrQuery : public BinaryQuery{ friend Query operator|(const Query&, const Query&); OrQuery(const Query& l, const Query& r) : BinaryQuery(l, r, "|"){ std::cout << "OrQuery" << std::endl; } QueryResult eval(const TextQuery&) const; }; inline Query operator|(const Query& lhs, const Query& rhs){ return std::shared_ptr<Query_base>(new OrQuery(lhs, rhs)); } #endif
Query.cpp
#include "Query.h" #include <algorithm> /* std::ostream& operator<<(std::ostream& os, const Query& q){ //Query::rep Make a virtual call to rep() through its query base pointer return os << q.rep(); } */ Query::Query(const std::string& s) : q(new WordQuery(s)){ std::cout << "Query pub" << std::endl; } QueryResult NotQuery::eval(const TextQuery& text)const{ //Virtual call to eval through Query operation object auto result = query.eval(text); //The result set is empty at the beginning auto ret_lines = std::make_shared<std::set<line_no>>(); auto beg = result.begin(); auto end = result.end(); //For each line of the input file, if the line is not in the result, it will be added to ret [lines] auto sz = result.get_file()->size(); for(size_t n = 0; n != sz; ++n){ //If you haven't finished processing all the lines of the result //Check if the current row exists if(beg == end || *beg != n){ ret_lines->insert(n); } else if(beg != end){ ++beg;//Continue to get the next line of reslut } } return QueryResult(rep(), ret_lines, result.get_file()); } QueryResult AndQuery::eval(const TextQuery& text)const{ //Virtual call through Query member lhs,rhs //Call eval to return QueryResult of each object auto right = rhs.eval(text); auto left = lhs.eval(text); //Save the set of left and right intersection auto ret_lines = std::make_shared<std::set<line_no>>(); //Writes the intersection of two ranges into a destination iteration. std::set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(*ret_lines, ret_lines->begin())); return QueryResult(rep(), ret_lines, left.get_file()); } QueryResult OrQuery::eval(const TextQuery& text)const{ //Virtual call through Query member lhs,rhs //Call eval to return QueryResult of each object auto right = rhs.eval(text); auto left = lhs.eval(text); //Copy the row number of the left operand to the result set auto ret_lines = std::make_shared<std::set<line_no>>(left.begin(), left.end()); //Line number from insert right operand ret_lines->insert(right.begin(), right.end()); //Returns a new QueryResult representing the union of lhs and rhs return QueryResult(rep(), ret_lines, right.get_file()); }
QueryResult.h
#ifndef __QUERYRESULT_H__ #define __QUERYRESULT_H__ #include <iostream> #include <set> #include <vector> #include <string> #include <memory> class QueryResult{ friend std::ostream& print(std::ostream&, const QueryResult&); public: using line_no = std::vector<std::string>::size_type; using Iter = std::set<line_no>::iterator; QueryResult(std::string s, std::shared_ptr<std::set<line_no>> p, std::shared_ptr<std::vector<std::string>> f): sought(s), lines(p), file(f){} Iter begin() const {return lines->begin();} Iter end() const {return lines->end();} std::shared_ptr<std::vector<std::string>> get_file() const{ return file; } private: std::string sought;//Query words std::shared_ptr<std::set<line_no>> lines;//Line number that appears std::shared_ptr<std::vector<std::string>> file; }; //Friend declaration of QueryResult class std::ostream& print(std::ostream&, const QueryResult&); #endif
TextQuery.h
#ifndef __TEXTQUERY_H__ #define __TEXTQUERY_H__ #include "QueryResult.h" #include <map> #include <iostream> #include <fstream> #include <sstream> #include <set> #include <vector> #include <string> #include <memory> using namespace std; class TextQuery{ public: using line_no = std::vector<std::string>::size_type; TextQuery(ifstream& is); QueryResult query(const std::string &sought) const; private: std::shared_ptr<std::vector<std::string>> file; std::map<std::string, std::shared_ptr<std::set<line_no>>> wm; }; #endif
TextQuery.cpp
#include "TextQuery.h" using namespace std; TextQuery::TextQuery(ifstream& is) : file(new vector<string>){ string text; while(getline(is, text)){//Read every line of the file file->push_back(text); int n = file->size() - 1;//Current line number istringstream line(text);//Break line text into words string word; while(line >> word){ //It's very important to use references, otherwise a new set will be copied to lines instead of the original one auto &lines = wm[word];//lines is shared? PTR if(!lines) lines.reset(new set<line_no>); lines->insert(n); } } } QueryResult TextQuery::query(const string &sought) const{ //If no fault is found, a smart pointer to this set is returned static shared_ptr<set<line_no>> nodata(new set<line_no>); auto ret = wm.find(sought); if(ret == wm.end()){ return QueryResult(sought, nodata, file);//Can't find } else{ return QueryResult(sought, ret->second, file); } }
main.cpp
#include "Query.h" //Friend function of QueryResult ostream& print(ostream& os, const QueryResult& qr){ os << qr.sought << " There are:" << qr.lines->size() << "second" << endl; for(auto num : *qr.lines){ os << "\t(Line number " << num + 1 << ")" << *(qr.file->cbegin() + num) << endl; } return os; } int main(){ ifstream infile("/home/ys/cpp/thread/oop/TextQuery/search_text"); TextQuery tq(infile); //Query q = Query("fiery") & Query("bird") | Query("wind");//OK //Query q = Query("fiery") | Query("bird");//OK //Query q("Daddy");//OK Query q = ~Query("Alice");//OK print(std::cout, q.eval(tq)) << std::endl; }
Compilation method:
g++ -g Query.cpp TextQuery.cpp main.cpp -std=c++11
Text file for query
Alice Emma has long flowing red hair. Her Daddy says when the wind blows through her hair, it looks almost alive, like a fiery bird in flight. A beautiful fiery bird, he tells her, magical but untamed. " Daddy , shush, there is no such thing," she tells him, at the same time wanting him to tell her more. Shyly, she asks, "I mean, Daddy , is there?"
Query q = query ("fiery") & Query ("bird") | query ("wind"); execution result:
(fiery & bird) | wind) appeared: twice (line 2)Her Daddy says when the wind blows (line 4)like a fiery bird in flight