《C++ Primer》文本查询程序 TextQuery

  1. 1. ★书中的一个疏漏
  2. 2. ★关于main函数的形参
  3. 3. ★关于<<操作符的重载
  4. 4. ★Query
  5. 5. ★执行程序
    1. 5.1. ◇习题15.41
    2. 5.2. ◇习题15.42(a)
    3. 5.3. ◇执行
  6. 6. ★完整代码

★书中的一个疏漏

代码敲打完成后准备编译测试,vs报错如图

error

百度之,该报错说的是Query(const string &);这个构造函数只作了声明,没有定义。

看到书上有这么一段话:

未定义

问题是,俺翻来覆去找了个遍,还是没有找到在定义WordQuery之后应该定义的该构造函数。

请教大牛后终于写了出来:

1
2
// 创建一个WordQuery对象,并且初始化use为1
Query(const string &s) : q(new WordQuery(s)), use(new size_t(1)) { }

其实就是初始化一下q和use,囧!

书中这个遗漏不知道坑了多少新手的爹!

★关于main函数的形参

一直习惯于在程序中使用cin直接输入参数,这次尝试了使用main形参。可以有三种效果相同的写法:

1
2
3
int main(int argc,char **argv)
int main(int argc,char *argv[])
int main(int argc,char argv[][])

忽然想起了鲁大师的经典段子:

孔乙己显出极高兴的样子,将两个指头的长指甲敲着柜台,点头说,“对呀对呀!……回字有四样写法,你知道么?”

★关于<<操作符的重载

复制控制的内容又遗忘了不少,复习总结一下加强记忆。

IO操作符必须为非成员函数。(14.2.1)

定义符合标准库iostream规范的输入或输出操作符时,必须使它成为非成员操作符,否者做操作数将只能是该类类型的对象:

1
2
3
// if operator << is a member of Sales_item
Sales_item iter;
iter << cout;

这个用法与输出操作符的正常使用方式相反!

而IO操作符通畅对非公用数据成员进行读写,因此,类通常将IO操作符设为友元。

在这个程序中,重载了<<操作符:

1
2
3
4
inline ostream& operator<<(ostream &os, const Query &q)
{
return q.display(os);
}

如果编写:

1
2
Query name(sought);
cout << name << endl;

name前的<<将调用Query输出操作符,该操作符执行q.display(os),将通过动态绑定执行q引用指向的对象相关联的display实例。

★Query

这个程序是《c++ primer》面向对象编程一章继书店程序后的第二个程序,也是整本书最复杂的一个。

各个类的功能:

class

继承层次:

继承层次

好在句柄类Query的使用使得对继承层次和多态性的使用简单不少,而句柄类和TextQuery分别由复制控制容器与算法两章的智能指针和简单TextQuery程序传承而来,而且综合使用了vector,map,set三种标准库容器泛型算法,复制控制,标准IO库也有较多应用,综合性比书店程序又增加了不少,可谓集该书精华于一身。

★执行程序

◇习题15.41

line_no size() const { return lines_of_text.size(); }

◇习题15.42(a)

基于句子:
void read_file_with_lines(ifstream &is) { store_file_with_lines(is); build_map(); }
基于单词:
void read_file_with_sentences(ifstream &is) { store_file_with_sentences(is); build_map(); }

◇执行

TextQuery执行效果

★完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <iterator>
using namespace std;
class TextQuery {
public:
truetypedef vector<string>::size_type line_no;
truevoid read_file_with_lines(ifstream &is) { store_file_with_lines(is); build_map(); }
truevoid read_file_with_sentences(ifstream &is) { store_file_with_sentences(is); build_map(); }
trueset<line_no> run_query(const string&) const;
truestring text_line(line_no) const;
trueline_no size() const { return lines_of_text.size(); }
private:
truevoid store_file_with_lines(ifstream&);
truevoid store_file_with_sentences(ifstream&);
truevoid build_map();
truevector<string> lines_of_text;
truemap< string, set<line_no> > word_map;
};
void TextQuery::store_file_with_lines(ifstream &is)
{
truestring text_line;
truewhile (getline(is, text_line))
truetruelines_of_text.push_back(text_line);
}
void TextQuery::store_file_with_sentences(ifstream &is)
{
truechar ws[] = { '\t', '\r', '\v', '\f', '\n' };
truechar eos[] = { '?', '.', '!' };
trueset<char> white_space(ws, ws + 5);
trueset<char> end_of_sentences(eos, eos + 3);
truestring sentence;
truechar ch;
truewhile (is.get(ch)) {
truetrueif (!white_space.count(ch))
truetruetruesentence += ch;
truetrueif (end_of_sentences.count(ch)) {
truetruetruelines_of_text.push_back(sentence);
truetruetruesentence.assign("");
truetrue}
true}
}
void TextQuery::build_map()
{
truefor (line_no line_num = 0; line_num != lines_of_text.size(); ++line_num) {
truetrueistringstream line(lines_of_text[line_num]);
truetruestring word;
truetruewhile (line >> word)
truetruetrueword_map[word].insert(line_num);
true}
}
set<TextQuery::line_no> TextQuery::run_query(const string &query_word) const
{
truemap< string, set<line_no> >::const_iterator loc = word_map.find(query_word);
trueif (loc == word_map.end())
truetruereturn set<line_no>();
trueelse
truetruereturn loc->second;
}
string TextQuery::text_line(line_no line) const
{
trueif (line < lines_of_text.size())
truetruereturn lines_of_text[line];
truethrow out_of_range("line number out of range");
}
class Query_base {
truefriend class Query;
protected:
truetypedef TextQuery::line_no line_no;
truevirtual ~Query_base() { }
private:
truevirtual set<line_no> eval(const TextQuery&) const = 0;
truevirtual ostream& display(ostream& = cout) const = 0;
};
class WordQuery : public Query_base {
truefriend class Query; // Query uses the WordQuery Constructor
trueWordQuery(const string &s) : query_word(s) { }
trueset<line_no> eval(const TextQuery &t) const { return t.run_query(query_word); }
trueostream& display(ostream &os) const { return os << query_word; }
truestring query_word;
};
class Query {
true// these operator need access to the Query_base * constructor
true// Query_base * constructor 为private,所以必须将操作符设为友元
truefriend Query operator~(const Query &);
truefriend Query operator|(const Query &, const Query &);
truefriend Query operator&(const Query &, const Query &);
public:
trueQuery(const string &s) : q(new WordQuery(s)), use(new size_t(1)) { } // 创建WordQuery对象
trueQuery(const Query &c) : q(c.q), use(c.use) { ++*use; }
true~Query() { decr_use(); }
trueQuery& operator=(const Query&);
trueset<TextQuery::line_no> eval(const TextQuery &t) const { return q->eval(t); }
trueostream& display(ostream &os) const { return q->display(os); }
private:
trueQuery(Query_base *query) : q(query), use(new size_t(1)) { }
trueQuery_base *q;
truesize_t *use;
truevoid decr_use() { if (--*use == 0) { delete q; delete use; } }
};
// 由于输出操作符不能是QueryBase类的成员,所以重载它并调用Query对象的虚函数display
inline ostream& operator<<(ostream &os, const Query &q)
{
truereturn q.display(os);
}
class NotQuery : public Query_base {
truefriend Query operator~(const Query&);
trueNotQuery(Query q) : query(q) { }
trueset<line_no> eval(const TextQuery&) const;
trueostream& display(ostream &os) const { return os << "~(" << query << ")"; }
trueconst Query query;
};
set<TextQuery::line_no> NotQuery::eval(const TextQuery &file) const
{
trueset<line_no> has_val = query.eval(file);
trueset<line_no> ret_lines;
truefor (line_no n = 0; n != file.size(); ++n)
truetrueif (has_val.find(n) == has_val.end())
truetruetrueret_lines.insert(n);
truereturn ret_lines;
}
// 没有定义eval,因此继承了一个纯虚函数,也是抽象类
class BinaryQuery : public Query_base {
protected:
trueBinaryQuery(Query left, Query right, string op) : lhs(left), rhs(right), oper(op) { }
trueostream& display(ostream &os) const { return os << "(" << lhs << " " << oper << " " << rhs << ")"; }
trueconst Query lhs, rhs;
trueconst string oper;
};
class AndQuery : public BinaryQuery {
truefriend Query operator&(const Query&, const Query&);
trueAndQuery(Query left, Query right) : BinaryQuery(left, right, "&") { }
trueset<line_no> eval(const TextQuery&) const;
};
set<TextQuery::line_no> AndQuery::eval(const TextQuery &file) const
{
trueset<line_no> left = lhs.eval(file), right = rhs.eval(file);
trueset<line_no> ret_lines;
trueset_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(ret_lines, ret_lines.begin()));
truereturn ret_lines;
}
class OrQuery : public BinaryQuery {
truefriend Query operator|(const Query&, const Query&);
trueOrQuery(Query left, Query right) : BinaryQuery(left, right, "|") { }
trueset<line_no> eval(const TextQuery&) const;
};
set<TextQuery::line_no> OrQuery::eval(const TextQuery &file) const
{
trueset<line_no> right = rhs.eval(file), ret_lines = lhs.eval(file);
trueret_lines.insert(right.begin(), right.end());
truereturn ret_lines;
}
inline Query operator~(const Query &oper)
{
true// Query_base *tmp = new NotQuery(expr);
true// return Query(emp);
truereturn new NotQuery(oper);
}
inline Query operator|(const Query &lhs, const Query &rhs)
{
truereturn new OrQuery(lhs, rhs);
}
inline Query operator&(const Query &lhs, const Query &rhs)
{
truereturn new AndQuery(lhs, rhs);
}
ifstream& open_file(ifstream &in, const string &file)
{
truein.close();
truein.clear();
truein.open(file.c_str());
truereturn in;
}
string make_plural(size_t ctr, const string &word, const string &ending) {
truereturn (ctr == 1) ? word : word + ending;
}
void print_result(const set<TextQuery::line_no> results, const Query &sought, const TextQuery &file)
{
truetypedef set<TextQuery::line_no> line_nums;
trueline_nums::size_type size = results.size();
truecout << "\n" << sought << " occurs " << size << " " << make_plural(size, "time", "s") << endl;
truefor (line_nums::const_iterator it = results.begin(); it != results.end(); ++it) {
truetruecout << "\t(line " << (*it) + 1 << ") " << file.text_line(*it) << endl;
true}
}
int main(int argc, char **argv)
{
trueifstream infile;
trueif (argc < 2 || !open_file(infile, argv[1])) {
truetruecerr << "No input file!" << endl;
truetruesystem("pause");
truetruereturn EXIT_FAILURE;
true}
trueTextQuery tq;
truetq.read_file_with_sentences(infile);
trueQuery andq = Query("I") | Query("at");
truecout << "\nExecuted Query For: " << andq << endl;
trueset<TextQuery::line_no> results = andq.eval(tq);
trueprint_result(results, andq, tq);
truesystem("pause");
truereturn 0;
}