《C++ Primer》中第15章为了讲解面向对象编程,举了一个例子:设计一个小程序,能够处理查询给定word在文件中所在行的任务,并且能够处理“非”查询,“或”查询,“与”查询。例如执行查询 one & of |the ,表示对单词one和of的查询结果取交集,然后对单词the的查询结果取并集。

  书中查询的底层操作实际定义在类TextQuery中,我们在TextQuery的基础上,进一步封装并实现如下图所示的类结构,能够达到上述功能需求。类之间的结构如下图所示:

  

  程序扼要设计如下表所示:

  在此基础之上,可以写出下述代码。代码已经详细注释,具体实现细节不再赘述。

#include <iostream>
#include <set>
#include <vector>
#include <fstream>
#include <sstream>
#include <string>
#include <map> using namespace std; class TextQuery {
public:
//执行查找,返回单词所在行号组成的set
set<int> run_query(const string &word) const {
auto it = word_map.find(word);
if (it == word_map.end())
return set<int>();
return it->second;
} //返回line行对应的字符串
string text_line(int line) const {
if (line < lines_of_text.size())
return lines_of_text[line];
throw out_of_range("line number out of range");
} //返回vector中存储的行的数目
int size() const {
return lines_of_text.size();
} //读取文件,建立键值对映射关系
void read_file(ifstream &is) {
store_file(is);
build_map();
} private:
//从文件输入流in中读取行,并将其存储在vector中
void store_file(ifstream &is) {
string textline;
while (getline(is, textline))
lines_of_text.push_back(textline);
} //建立单词 和行号组成的键值对映射关系
void build_map() {
for (int line_no = ; line_no != lines_of_text.size(); line_no++) {
string text_line = lines_of_text[line_no];
istringstream line(text_line);
string word;
while (line >> word) {
word_map[word].insert(line_no);
} }
} //存储所有的行
vector<string> lines_of_text;
//存储单词,行号键值对
map<string, set<int>> word_map;
}; //显示查询结果
void print_results(const set<int> &locs, const TextQuery &file) {
if (locs.empty()) {
cout << endl << "Sorry. There are no entries for your query." << endl << "Try again." << endl;
return;
}
//输出行号和对应的行
for (auto it = locs.begin(); it != locs.end(); it++) {
cout << "\t" << "(line "
<< (*it) + << ") "
<< file.text_line(*it) << endl;
}
} //打开文件,返回文件输入流
ifstream &open_file(ifstream &in, const string &file) {
in.close();
in.clear();
in.open(file.c_str());
return in;
} class Query_base {
friend class Query; protected:
virtual ~Query_base() { } private:
//定义纯虚函数,对TextQuery对象执行查询
virtual set<int> eval(const TextQuery &) const = ;
//输出查询结果
virtual ostream &display(ostream & = cout) const = ;
};
//对单词执行查询
class WordQuery : public Query_base {
friend class Query; WordQuery(const string &s) : query_word(s) { } //执行查询,返回查询的结果
set<int> eval(const TextQuery &t) const {
return t.run_query(query_word);
}
//输出查询结果
ostream &display(ostream &os) const {
return os << query_word;
}
//要查询的单词
string query_word;
}; class Query {
//这些函数能够隐式调用private中的构造函数 Query(Query_base *query),创建Query对象
friend Query operator~(const Query &); friend Query operator|(const Query &, const Query &); friend Query operator&(const Query &, const Query &); public:
//对要查询的单词初始化Query对象
Query(const string &s) : q(new WordQuery(s)), use(new int()) { }
//复制构造函数
Query(const Query &c) : q(c.q), use(c.use) {
++*use;
}
//析构函数
~Query() {
decr_use();
}
//重载运算符=
Query &operator=(const Query &);
//对TextQuery 执行查询任务
set<int> eval(const TextQuery &t) const {
return q->eval(t);
} ostream &display(ostream &os) const {
return q->display(os);
} private:
//友元重载函数可以访问
Query(Query_base *query) : q(query), use(new int()) { } Query_base *q;
int *use;
//减少引用计数的值,如果引用计数的值为0,那么删除对象
void decr_use() {
if (--*use == ) {
delete q;
delete use;
}
}
};
//重载运算符 = ,减少本对象的引用计数,同时增加要复制的对象的引用计数
Query &Query::operator=(const Query &rhs) {
++*rhs.use;
decr_use();
q = rhs.q;
use = rhs.use;
return *this;
}
//重载运算符 <<
ostream &operator<<(ostream &os, const Query &q) {
return q.display(os);
} class BinaryQuery : public Query_base {
protected:
BinaryQuery(Query left, Query right, string op) : lhs(left), rhs(right), oper(op) { }
//输出 查询 操作
ostream &display(ostream &os) const {
return os << "(" << lhs << " " << oper << " " << rhs << ")";
}
//左右两个操作数
const Query lhs, rhs;
//运算符
const string oper;
}; class AndQuery : public BinaryQuery {
//友元重载运算符函数可以访问构造器函数 Query(Query_base *query)
friend Query operator&(const Query &, const Query &); AndQuery(Query left, Query right) : BinaryQuery(left, right, "&") { }
//查询实际上是对左右操作数的查询结果取交集
set<int> eval(const TextQuery &file) const {
set<int> left = lhs.eval(file);
set<int> right = rhs.eval(file);
set<int> ret;
set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(ret, ret.begin()));
return ret;
}
}; class OrQuery : public BinaryQuery {
//友元重载运算符函数可以访问构造器函数Query(Query_base *query)
friend Query operator|(const Query &, const Query &); OrQuery(Query left, Query right) : BinaryQuery(left, right, "|") { }
//查询实际上是对左右操作数的查询结果取并集
set<int> eval(const TextQuery &file) const {
set<int> left = lhs.eval(file);
set<int> right = rhs.eval(file);
left.insert(right.begin(), right.end());
return left;
}
}; class NotQuery : public Query_base {
//友元重载运算符函数可以访问构造器函数 Query(Query_base *query)
friend Query operator~(const Query &); NotQuery(Query q) : query(q) { };
//执行的查询实际上是对左右两个操作数的查询结果取差集
set<int> eval(const TextQuery &file) const {
auto result = query.eval(file);
set<int> ret;
for (int n = ; n != file.size(); n++) {
if (result.find(n) == result.end())
ret.insert(n);
}
return ret;
} ostream &display(ostream &os) const {
return os << "~" << query << ")";
} const Query query;
}; //重载运算符 &
inline Query operator&(const Query &lhs, const Query &rhs) {
return new AndQuery(lhs, rhs);
} //重载运算符 |
inline Query operator|(const Query &lhs, const Query &rhs) {
return new OrQuery(lhs, rhs);
} //重载运算符 ~
inline Query operator~(const Query &oper) {
return new NotQuery(oper);
} //创建TextQuery实例
TextQuery build_textfile(const string &filename) {
ifstream infile;
if (!open_file(infile, filename)) {
cerr << "can't open input file!" << endl;
return TextQuery();
}
TextQuery ret;
ret.read_file(infile);
return ret;
} int main() {
TextQuery file = build_textfile("/Users/zhouyang/Desktop/text.txt");
string word1, word2, word3;
while (cin >> word1 >> word2 >> word3) {
Query q = Query(word1) & Query(word2) | Query(word3);
cout << "Executing Query for: " << q << endl;
auto result = q.eval(file);
print_results(result, file);
}
return ;
}

执行查询: one & of |the , 结果如下:

one of the
Executing Query for: ((one & of) | the)
(line 1) Many in the US believe the use of the nuclear bomb, though devastating, was right, because it forced Japan to surrender, bringing an end to World War Two.
(line 2) The daughter of one survivor, who was visiting the memorial on Friday, said the suffering had "carried on over the generations".
(line 3) "That is what I want President Obama to know," Han Jeong-soon, 58, told the Associated Press news agency. "I want him to understand our sufferings."
(line 4) Seiki Sato, whose father was orphaned by the bomb, told the New York Times: "We Japanese did terrible, terrible things all over Asia. That is true. And we Japanese should say we are sorry because we are so ashamed, and we have not apologised sincerely to all these Asian countries. But the dropping of the atomic bomb was completely evil."
(line 6) Media captionFilmmaker Barry Frechette told Shigeaki Mori's story in the film Paper Lanterns (line 9) China responded to the visit by saying that Japan's six-week attack on the Chinese city of Nanjing, which began in December 1937, was more worthy of reflection.
(line 10) The Chinese say 300,000 people were killed, although other sources say the figure was lower.
(line 12) 'Just listen' - Japan's media on the visit
(line 13) The Chugoku Shimbun urges Mr Obama to "hear the voices of Hiroshima". "The people of Hiroshima will be watching the president closely, eyeing to what extent he is truly resolved to advance the abolition of nuclear arms," it said.
(line 14) The Asahi Shimbun carries an article saying Mr Obama's "gestures will shape the visit", with the "most powerful gesture" being to "just listen to the bomb victims' memories of suffering and activism".
(line 15) The Japan Times says: "To truly pay homage to those whose lives were lost or irrevocably altered by the Hiroshima and Nagasaki bombings, Obama's visit must galvanise the international community to move without delay toward a world free of nuclear weapons. The fact that these weapons have not been used over the past 70 years does not guarantee a risk-free future for our children."
(line 20) The bomb was nicknamed "Little Boy" and was thought to have the explosive force of 20,000 tonnes of TNT
(line 21) Paul Tibbets, a 30-year-old colonel from Illinois, led the mission to drop the atomic bomb on Japan
(line 22) The Enola Gay, the plane which dropped the bomb, was named in tribute to Col Tibbets' mother
(line 23) The final target was decided less than an hour before the bomb was dropped. The good weather conditions over Hiroshima sealed the city's fate
(line 24) On detonation, the temperature at the burst-point of the bomb was several million degrees. Thousands of people on the ground were killed or injured instantly
(line 25) The hours before the bomb was dropped

《C++ Primer》 chapter 15 TextQuery的更多相关文章

  1. 《算法导论》— Chapter 15 动态规划

    序 算法导论一书的第四部分-高级设计和分析技术从本章开始讨论,主要分析高效算法的三种重要技术:动态规划.贪心算法以及平摊分析三种. 首先,本章讨论动态规划,它是通过组合子问题的解而解决整个问题的,通常 ...

  2. 《C++ Primer》Chapter 7 [类]

    前言 在C++中,我们使用类定义自己得数据类型/通过定义新的类型来反应待解决的题的各种概念,是我们更容易编写.调试和修改程序. 我们需要主要关注数据抽象的重要性.数据抽象能帮助我们将对象的具体实现与对 ...

  3. [笔记] 《c++ primer》书店程序 Chapter 1

    书店程序是<c++ primer>中重要的实例,涉及大部分重要知识点,但代码分散阅读不便,下面按照章节顺序总结 Sales_item.h #ifndef SALESITEM_H // we ...

  4. 《C++ Primer》学习总结;兼论如何使用'书'这种帮助性资料

    6.25~ 6.27,用了3天翻了一遍<C++ Primer>. ▶书的 固有坏处 一句话: 代码比 文字描述 好看多了.————> 直接看习题部分/ 看demo就行了 看文字在描述 ...

  5. 《C++Primer》第五版习题答案--第五章【学习笔记】

    <C++Primer>第五版习题答案--第五章[学习笔记] ps:答案是个人在学习过程中书写,可能存在错漏之处,仅作参考. 作者:cosefy Date: 2020/1/15 第五章:语句 ...

  6. 《C++Primer》第五版习题答案--第六章【学习笔记】

    <C++Primer>第五版习题答案--第六章[学习笔记] ps:答案是个人在学习过程中书写,可能存在错漏之处,仅作参考. 作者:cosefy Date: 2020/1/16 第六章:函数 ...

  7. 再读《C++ Primer》——变量和基本类型

    刚上大学那时,几个室友一块买了本<C++ Primer>第4版,看了一遍后就没怎么碰了,偶尔拿出来翻翻,当作工具书使用.后来知道有第5版了,一直觉得内容差不多吧.直到最近,再读其中的一些内 ...

  8. 《C++ Primer》学习笔记 :命名空间的using声明

    最近在学C++,在<C++ Primer>第五版的3.1节中说到使用using声明来使用命名空间中的成员,<C++ Primer>中这样写道: 有了using声明就无须专门的前 ...

  9. 《C++ Primer》学习笔记:迭代器介绍

    <C++Primer>(第五版)中,3.4.1的例题中使用一个名为text的字符串向量存放文本文件中的数据,输出text中的内容,刚开始我这样写: #include <iostrea ...

随机推荐

  1. java反射简解

    1.首先一个问题,什么是类,类是不是对象? 我们总是说我们new一个对象出来 那么我们还没有new的时候,建造的那个java类是不是对象呢? 是 它是java.lang.Class的对象 对于反射我们 ...

  2. HDU1372,BFS象棋马走日

    简单的对于bfs的运用,但是还是写的太慢了写了TMD的1H,主要是不熟悉,以后慢慢熟悉就好了,模型基本已经能建立了,主要出现bug是在方向数组的运用上面,一定要记得是从0开始的,而不是从1开始的,导致 ...

  3. hdu_4897_Little Devil I(树链剖分)

    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=4897 题意:有三种操作,1是在树上的两个节点之间的路径改变当前的颜色,2是改变树上有且只有一个端点在u ...

  4. 详细讲解MOSFET管驱动电路(转)

    作者:   来源:电源网 关键字:MOSFET 结构 开关 驱动电路 在使用MOS管设计开关电源或者马达驱动电路的时候,大部分人都会考虑MOS的导通电阻,最大电压等,最大电流等,也有很多人仅仅考虑这些 ...

  5. 【矩阵压缩】 poj 1050

    题意:给一个矩阵,里面有正负数,求子矩阵和的最大值 #include <iostream> #include <cstdio> #include <stdlib.h> ...

  6. apk文件分析原则

    如果在dex生成的jar文件里没有发现关键内容的话,就要注意jar里面的native函数以及loadlibrary操作,从而可以判断出加载了哪些so,调用了什么函数.就不会出现判断不出是不是加载了某s ...

  7. Hadoop: failed on connection exception: java.net.ConnectException: Connection refuse

    ssh那些都已经搞了,跑一个书上的例子出现了Connection Refused异常,如下: 12/04/09 01:00:54 INFO ipc.Client: Retrying connect t ...

  8. 转:Visual Studio进行Web性能测试- Part II

    原文作者:Ambily.raj 对于一个多用户的应用程序,性能是非常重要的.性能不仅是执行的速度,它包括负载和并发方面.Visual Studio是可以用于性能测试的工具之一.Visual Studi ...

  9. 转:web_submit_data函数

    web_submit_data函数处理无状态或者上下文无关的表单提交.它用来生成表单的GET或POST请求,这些请求与Form自动生成的请求是一样的.发送这些请求时不需要表单上下文. 函数语法:Int ...

  10. 配置nova instances使用NFS后端

    首先先使用“nova delete”命令删除所有实例,释放磁盘空间. 停止nova服务:service libvirtd stopservice openstack-nova-compute stop ...