先附上前面的basket类的例子,里面很多思想也是我需要学的
#include <iostream> #include <string> #include <set> #include <memory> //买书的类 class quote { friend double print_total(std::ostream &os, quote &q, std::size_t sz); public: quote() = default; quote(const std::string &book, double p): bookNo(book), price(p) { } quote(const quote &q): bookNo(q.bookNo), price(q.price) { } quote(quote &&q): //一个右值拷贝 bookNo(q.bookNo), price(q.price) { } quote& operator=(const quote &q) { bookNo = q.bookNo; price = q.price; return *this; } virtual double net_price(std::size_t sz)const { return price * sz; } const std::string& isbn()const { return bookNo; } //注意动态内存分配问题,基类和派生类的内存大小不同 //所以我们写了个分配函数,且必须定义为虚函数。为了实现后面的动态分配 //后面跟着成员限定符说明是用作左值还是右值 virtual quote* clone()const& { return new quote(*this); } virtual quote* clone() && { return new quote(std::move(*this)); } protected: double price; private: std::string bookNo; }; double print_total(std::ostream &os, quote &q, std::size_t sz) { double ret = q.net_price(sz); os << q.isbn() << " " << q.price << " " << ret << "\n"; return ret; } //抽象基类,因为打折有许多不同的措施,但都基于discount和disc_num //简单来说抽象出来一个标准,discount和disc_num 就可以说是一个标准。 class disc_quote : public quote { public: disc_quote() = default; disc_quote(const std::string &book, double p, double disc, std::size_t num): quote(book, p), discount(disc), disc_num(num) { } double net_price(std::size_t sz)const = 0; //纯虚函数 protected: double discount; std::size_t disc_num; private: }; //第一种打折方式 class one_bulk_quote : public disc_quote { public: using disc_quote::disc_quote; double net_price(std::size_t sz)const override { if(sz > disc_num) { return sz * price * discount; } else { return sz * price; } } //对第一种打折类实现动态分配 one_bulk_quote* clone()const & { return new one_bulk_quote(*this); } one_bulk_quote* clone() && { return new one_bulk_quote(std::move(*this)); } protected: private: }; class two_bulk_quote : public disc_quote { public: using disc_quote::disc_quote; double net_price(std::size_t sz) { if(sz > disc_num) { return (sz-disc_num) * price * discount + disc_num * price; } else { return sz * price; } } protected: private: }; class basket { public: //void add_item(const std::shared_ptr<quote>&sale) //{ // items.insert(sale); //} //如果像上面那样写分配内存不会是动态决定的,参数传递是quote,那么无论传参是什么分配都是quote,如果是派生类就会被砍掉一部分。 void add_item(const quote &sale) { items.insert(std::shared_ptr<quote>(sale.clone())); } void add_item(quote && sale) { items.insert(std::shared_ptr<quote>(std::move(sale).clone())); } double total_receipt(std::ostream &os)const; protected: private: //自定义比较函数 static bool compare(const std::shared_ptr<quote> &lhs, const std::shared_ptr<quote> &rhs) { return lhs->isbn() < rhs->isbn(); } std::multiset<std::shared_ptr<quote>, decltype(compare)*> items{compare};//decltype不能推断出指针和引用 }; double basket::total_receipt(std::ostream &os)const { double sum = 0.0; //注意下upper_bound的作用 for(auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter)) { sum += print_total(std::cout, **iter, items.count(*iter)); } os << "total Sale: " << sum << std::endl; return sum; } int main() { }
文本程序
#ifndef _TEXTQUERY_H_ #define _TEXTQUERY_H_ #include <iostream> #include <map> #include <set> #include <fstream> #include <sstream> #include <memory> #include <vector> #include <string> #include <algorithm> class QueryResult; class TextQuery; using line_no = std::vector<std::string>::size_type; class QueryResult { friend class TextQuery; friend std::ostream& operator<<(std::ostream &os, const QueryResult &q); public: QueryResult() = default; QueryResult(std::string s, std::shared_ptr<std::set<line_no>>li, std::shared_ptr<std::vector<std::string>>wf): q_word(s), lines(li), wordfile(wf) { } std::set<line_no>::iterator begin(); std::set<line_no>::iterator end(); std::shared_ptr<std::vector<std::string>> get_file(); private: std::string q_word; //要查询单词 std::shared_ptr<std::set<line_no>>lines; //出现行号 std::shared_ptr<std::vector<std::string>>wordfile; //单词文件 }; class TextQuery { friend class QueryResult; public: TextQuery() = default; TextQuery(std::ifstream &is); QueryResult query(const std::string &s)const; private: std::shared_ptr<std::vector<std::string>>m_file; std::map<std::string, std::shared_ptr<std::set<line_no>>>m_word_line; //考虑共享数据的需求 }; //Query_base 抽象基类,制定一个标准 class Query_base { friend class Query; protected: virtual ~Query_base() = default; private: // virtual QueryResult eval(const TextQuery &)const = 0; virtual std::string rep()const = 0; }; //接口类,基类包括派生类都可以通过Query来使用,隐藏实现 class Query { friend Query operator~(const Query &); friend Query operator&(const Query &, const Query &); friend Query operator|(const Query &, const Query &); friend std::ostream& operator<<(std::ostream &os, const Query &query); public: Query(const std::string&);//wait WordQuery //Query是Query_base的唯一接口,所以必须定义纯虚函数 //且Query就可以通过指针来调用派生类各自Query_base虚函数 QueryResult eval(const TextQuery &t)const { return q->eval(t); } std::string rep()const { return q->rep(); } private: //构造函数定义为private,不希望一般用户随便定义Query_base对象。 Query(std::shared_ptr<Query_base> query):q(query) { } //通过智能指针实现动态绑定 std::shared_ptr<Query_base> q; }; //派生类 class WordQuery: public Query_base { friend class Query; WordQuery(const std::string &s):query_word(s) { } //具体的类 QueryResult eval(const TextQuery &t)const { return t.query(query_word); } std::string rep()const { return query_word; } std::string query_word; //要查找的单词 }; //无论哪种查询都是建立在WordQuery的根本上,So Query的构造函数用一个WordQuery来初始化 inline Query::Query(const std::string &s): q(new WordQuery(s)) { } class NotQuery: public Query_base { friend Query operator~(const Query &); NotQuery(const Query &q):query(q) { } //具体的类,覆盖掉纯虚函数 std::string rep()const { return "~(" + query.rep() + ")"; } QueryResult eval(const TextQuery &)const; Query query; }; inline Query operator~(const Query &operand) { //注意返回值 return std::shared_ptr<Query_base>(new NotQuery(operand)); } //两个运算符的抽象基类 class BinaryQuery: public Query_base { protected: BinaryQuery(const Query &l, const Query &r, std::string s): lhs(l), rhs(r), opSym(s) { } std::string rep()const { return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; } //eval函数没有定义,继承了纯虚函数,还是抽象基类 Query lhs, rhs; std::string opSym; }; class AndQuery: public BinaryQuery { friend Query operator&(const Query&, const Query&); AndQuery(const Query &left, const Query &right): BinaryQuery(left, right, "&") { } //具体的类,覆盖了eval并且继承了rep 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 &left, const Query &right): BinaryQuery(left, right, "|") { } //具体的类,覆盖了eval并且继承了rep QueryResult eval(const TextQuery&)const; }; inline Query operator|(const Query&lhs, const Query&rhs) { return std::shared_ptr<Query_base>(new OrQuery(lhs, rhs)); }
#include "textquery.h" std::ostream& operator<<(std::ostream &os, const QueryResult &q) { os << "element occurs:" << q.lines->size() << " times\n"; for(line_no i : *q.lines) { os << " " << "line(" << i+1 << ") "; os << *(q.wordfile->begin()+i) << "\n"; //os << (*(q.wordfile))[i] << "\n"; //注意运算符优先级 } return os; } TextQuery::TextQuery(std::ifstream &is):m_file(new std::vector<std::string>) { std::string word; std::string line; static int i = 0; while(std::getline(is, line)) { //行号 m_file->push_back(line); i = m_file->size()-1; std::istringstream ist(line); while(ist >> word) { auto &li = m_word_line[word]; //返回set的shared_ptr if(!li) li.reset(new std::set<line_no>); li->insert(i); } } } QueryResult TextQuery::query(const std::string &s)const { static std::shared_ptr<std::set<line_no>> nodata(new std::set<line_no>); auto ret = m_word_line.find(s); if(ret == m_word_line.end()) return QueryResult(s, nodata, m_file); else return QueryResult(s, ret->second, m_file); } std::ostream& operator<<(std::ostream &os, const Query &query) { //query.rep()内部还是通过指针来调用的,是需调用 return os << query.rep(); } //QueryResult std::set<line_no>::iterator QueryResult::begin() { return lines->begin(); } std::set<line_no>::iterator QueryResult::end() { return lines->end(); } std::shared_ptr<std::vector<std::string>> QueryResult::get_file() { return wordfile; } //eval QueryResult OrQuery::eval(const TextQuery &text)const { auto right = rhs.eval(text), left = lhs.eval(text); auto ret_lines = std::make_shared<std::set<line_no>>(left.begin(), left.end()); ret_lines->insert(right.begin(), right.end()); return QueryResult(rep(), ret_lines, left.get_file()); } QueryResult AndQuery::eval(const TextQuery &text)const { auto left = lhs.eval(text), right = rhs.eval(text); auto ret_lines = std::make_shared<std::set<line_no>>(); 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 NotQuery::eval(const TextQuery &text)const { auto result = query.eval(text); auto ret_lines = std::make_shared<std::set<line_no>>(); auto beg = result.begin(), end = result.end(); auto sz = result.get_file()->size(); //相当于两个集合两个指针一起走。 for(std::size_t n = 0; n != sz; ++n) { if(beg == end || *beg != n) ret_lines->insert(n); else if(beg != end) ++beg; } return QueryResult(rep(), ret_lines, result.get_file()); }
#include "textquery.h" int main(int argc, char *argv[]) { std::ifstream is(argv[1]); TextQuery t1(is); std::string word; Query q = Query("fiery") & Query("bird") | Query("wind"); //Query q = Query("fiery"); //Query q = Query("A"); std::cout << q << std::endl; std::cout << q.eval(t1) << std::endl; }
运行结果:
这个例子自己也看了挺久,感觉还是有点复杂,可能是自己写的这种面向对象的不多吧,还要多加联系。
这个例子中的很多思想非常好,值得我们去学习。
总结:
1.好好体会抽象基类纯虚函数这个概念,在c++面向对象编程是非常重要的。