C++ Primer 中文版(第 5 版)练习解答合集
自己写的解答,如有错误之处,烦请在评论区指正!
1
都包含 4 个元素。实际上 b1 和 b2 的智能指针成员指向的是同一个 vector
2
// StrBlob.h
#ifndef STRBLOB
#define STRBLOB
#include <stdexcept>
#include <memory>
#include <string>
#include <vector>
using std::vector;
using std::string;
using std::initializer_list;
using std::shared_ptr;
using std::make_shared;
class StrBlob {
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string &t) {
data->push_back(t);
}
void pop_back();
string& front();
string& back();
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string &msg) const;
};
StrBlob::StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob::StrBlob(initializer_list<string> il) :
data(make_shared<vector<string>>(il)) { }
void StrBlob::check(size_type i, const string &msg) const {
if (i >= data->size())
throw std::out_of_range(msg);
}
string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
const string& StrBlob::front() const {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
const string& StrBlob::back() const {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
return data->pop_back();
}
#endif // STRBLOB
3
不需要。因为这两个函数都会改变底层 vector 的数据,所以如果一个 StrBlob 对象本身是 const 的,就没有通过该对象来修改底层数据的需求。
4
因为 i
是 vector<string>::size_type
类型,该类型为无符号类型,不存在负数的可能性。
5
将构造函数写成 explicit 的优点在于允许了 string 的初始化列表到 StrBlob 类型的隐式转换
缺点在于可能会有意料之外的隐式转换造成问题。此外,对 explicit 构造函数,只能直接初始化(用圆括号),不能拷贝初始化(等于)。
6
#include <iostream>
#include <vector>
using namespace std;
vector<int>* getNewVec() {
vector<int>* pvec(new vector<int>());
return pvec;
}
vector<int>* readVec(istream& is, vector<int>* pvec) {
int buffer;
while (is >> buffer)
pvec->emplace_back(buffer);
return pvec;
}
void displayVec(ostream& os, vector<int>* pvec) {
for (auto num : *pvec)
cout << num << " ";
cout << endl;
}
int main() {
vector<int>* pvec = getNewVec();
displayVec(cout, readVec(cin, pvec));
delete pvec;
return 0;
}
7
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
shared_ptr<vector<int>> getNewVec() {
shared_ptr<vector<int>> pvec = make_shared<vector<int>>();
return pvec;
}
shared_ptr<vector<int>> readVec(istream& is, shared_ptr<vector<int>> pvec) {
int buffer;
while (is >> buffer)
pvec->emplace_back(buffer);
return pvec;
}
void displayVec(ostream& os, shared_ptr<vector<int>> pvec) {
for (auto num : *pvec)
cout << num << " ";
cout << endl;
}
int main() {
shared_ptr<vector<int>> pvec = getNewVec();
displayVec(cout, readVec(cin, pvec));
return 0;
}
8
发生了内存泄漏。函数会根据 p 是否是空指针返回一个 bool 值,但是调用函数的主函数无法使用 p 所指向的内存,也无法 delete p 指向的内存。
9
前两行代码发生了内存泄漏,将 q 赋给 r 之后,原本 r 指向的内存空间永远不可能被归还了。
后两行代码没有问题,将 q2 付给 r2 之后,智能指针 r2 对应的计数器变成 0,编译器自动释放了原来 r2 指向的内存空间。
10
此调用是正确的。首先 p 是一个智能指针,指向 int 类型,值为 42 的内存,在调用前引用计数是 1。调用时创建了一个临时的智能指针,和 p 指向相同的内存,此时引用计数是 2。调用结束后智能指针被自动销毁,引用计数变回 1,即 p 是唯一指向内存的指针。
11
会创建一个临时的智能指针指向和 p 相同的内存,但是因为使用 get 成员函数取出了智能指针底层的指针,使得这个临时的智能指针和 p 之间相互独立,引用计数都是 1。这样在调用结束后,临时的智能指针被销毁,引用计数变成 0,内存被 delete,p 变成了空悬指针,可能会引发错误。
12
(a)合法。正常使用智能指针。
(b)不合法,不存在从普通指针到智能指针的隐式转换。
(c)不合法,同上。
(d)合法。但是调用结束后 p 会变成空悬指针。
13
sp 会变成空悬指针。
14
#include <iostream>
#include <memory>
using namespace std;
struct destination{
int port;
int length;
};
struct connection{
destination* dest;
int connect_time = 0;
connection(destination* d) : dest(d) { }
};
connection connect(destination* d) {
connection ret(d);
return ret;
}
void disconnect(connection c) {
c.dest = nullptr;
c.connect_time = 0;
}
void end_connection(connection *p) {
disconnect(*p);
}
int main() {
destination d1 = {443, 100};
connection c = connect(&d1);
shared_ptr<connection> p(&c, end_connection);
return 0;
}
15
int main() {
destination d1 = {443, 100};
connection c = connect(&d1);
shared_ptr<connection> p(&c,
[](connection *p){
p->dest = nullptr;
p->connect_time = 0;
});
return 0;
}
16
// 错误代码
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> up(new int(42));
unique_ptr<int> up1 = up;
return 0;
}
error 信息如下:
[Error] use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]'
17
(a)非法。unique_ptr 的初始化需要接收一个指针
(b)合法。
(c)合法。注意 pi2 后续可能成为空悬指针
(d)合法。注意 pi 后续可能成为空悬指针
(e)合法。
(f)合法。注意 p2 后续可能成为类似空悬指针的 unique_ptr
18
因为 shared_ptr 允许多个智能指针指向同一个内存空间,可以简单地赋值或拷贝。而 unique_ptr 则限制了同一块内存空间只能被一个 unique_ptr 指着,所以当我们需要转移一个 unique_ptr 指针的所有权的时候,就需要使用 release 成员来解除指针和内存空间之间的绑定,并返回一个指针来让另一个指针接管。
19
#ifndef STRBLOB
#define STRBLOB
#include <stdexcept>
#include <memory>
#include <string>
#include <vector>
using std::size_t;
using std::vector;
using std::string;
using std::initializer_list;
using std::shared_ptr;
using std::make_shared;
using std::weak_ptr;
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string &t) {
data->push_back(t);
}
void pop_back();
string& front();
const string& front() const;
string& back();
const string& back() const;
StrBlobPtr begin();
StrBlobPtr end();
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string &msg) const;
};
StrBlob::StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob::StrBlob(initializer_list<string> il) :
data(make_shared<vector<string>>(il)) { }
void StrBlob::check(size_type i, const string &msg) const {
if (i >= data->size())
throw std::out_of_range(msg);
}
string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
const string& StrBlob::front() const {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
const string& StrBlob::back() const {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
return data->pop_back();
}
// StrBlobPtr
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob& a, size_t sz = 0) :
wptr(a.data), curr(sz) { }
string& deref() const;
StrBlobPtr& incr();
bool equ(const StrBlobPtr& rhs) {
auto spl = this->wptr.lock();
auto spr = rhs.wptr.lock();
if (spl != spr)
return false;
else
return this->curr == rhs.curr;
}
bool neq(const StrBlobPtr& rhs) {
return !(this->equ(rhs));
}
private:
shared_ptr<vector<string>> check(size_t i, const string& msg) const;
weak_ptr<vector<string>> wptr;
size_t curr;
};
shared_ptr<vector<string>>
StrBlobPtr::check(size_t i, const string& msg) const {
auto ret = wptr.lock();
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret;
}
string& StrBlobPtr::deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& StrBlobPtr::incr() {
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() {
return StrBlobPtr(*this, data->size());
}
#endif // STRBLOB
20
#include <iostream>
#include <fstream>
#include <string>
#include "StrBlob.h"
using namespace std;
int main() {
StrBlob strBlob;
ifstream ifs("data.d");
string buffer;
while (getline(ifs, buffer)) {
strBlob.push_back(buffer);
}
StrBlobPtr ps(strBlob);
while (ps.neq(strBlob.end())) {
cout << ps.deref() << endl;
ps.incr();
}
return 0;
}
21
改写后的语句可读性太差,不利于修改和二次开发。
22
#include <iostream>
#include <fstream>
#include <string>
#include "1222.h"
using namespace std;
int main() {
const StrBlob constStrBlob({"111", "222"});
StrBlobPtr ps(constStrBlob);
while (ps.neq(constStrBlob.end())) {
cout << ps.deref() << endl;
ps.incr();
}
return 0;
}
// 1222.h
#ifndef STRBLOB
#define STRBLOB
#include <stdexcept>
#include <memory>
#include <string>
#include <vector>
using std::size_t;
using std::vector;
using std::string;
using std::initializer_list;
using std::shared_ptr;
using std::make_shared;
using std::weak_ptr;
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string &t) {
data->push_back(t);
}
void pop_back();
string& front();
const string& front() const;
string& back();
const string& back() const;
StrBlobPtr begin() const;
StrBlobPtr end() const;
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string &msg) const;
};
StrBlob::StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob::StrBlob(initializer_list<string> il) :
data(make_shared<vector<string>>(il)) { }
void StrBlob::check(size_type i, const string &msg) const {
if (i >= data->size())
throw std::out_of_range(msg);
}
string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
const string& StrBlob::front() const {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
const string& StrBlob::back() const {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
return data->pop_back();
}
// StrBlobPtr
class StrBlobPtr {
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob& a, size_t sz = 0) :
wptr(a.data), curr(sz) { }
StrBlobPtr(const StrBlob& a, size_t sz = 0) :
wptr(a.data), curr(sz) { }
string& deref() const;
StrBlobPtr& incr();
bool equ(const StrBlobPtr& rhs) const {
auto spl = this->wptr.lock();
auto spr = rhs.wptr.lock();
if (spl != spr)
return false;
else
return this->curr == rhs.curr;
}
bool neq(const StrBlobPtr& rhs) const {
return !(this->equ(rhs));
}
private:
shared_ptr<vector<string>> check(size_t i, const string& msg) const;
weak_ptr<vector<string>> wptr;
size_t curr;
};
shared_ptr<vector<string>>
StrBlobPtr::check(size_t i, const string& msg) const {
auto ret = wptr.lock();
if (!ret)
throw std::runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw std::out_of_range(msg);
return ret;
}
string& StrBlobPtr::deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& StrBlobPtr::incr() {
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr StrBlob::begin() const {
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end() const {
return StrBlobPtr(*this, data->size());
}
#endif // STRBLOB
23
#include <iostream>
#include <memory>
#include <cstring>
using namespace std;
char* myConcatenate(char* a, char* b) {
const int sizeA = strlen(a), sizeB = strlen(b);
char* ret = new char[sizeA + sizeB + 1];
int p = 0;
for (int i = 0; i < sizeA; ++i)
ret[p++] = a[i];
for (int i = 0; i < sizeB; ++i)
ret[p++] = b[i];
ret[p] = '\0';
return ret;
}
int main() {
char a[] = "hello ";
char b[] = "world!";
char* c = myConcatenate(a, b);
printf("%s\n", c);
return 0;
}
string 对象:
#include <iostream>
#include <string>
using namespace std;
string myConcatenate(const string a, const string b) {
return a + b;
}
int main() {
string a("Hello ");
string b("world!");
cout << myConcatenate(a, b) << endl;
return 0;
}
24
#include <iostream>
using namespace std;
int main() {
int maxSize = 16;
char* p = new char[maxSize];
string buffer;
while (cin >> buffer) {
if (maxSize - 1 < buffer.size()) {
while (maxSize - 1 < buffer.size())
maxSize <<= 1;
char* tmp = new char[maxSize];
delete[] p;
p = tmp;
}
for (int i = 0; i < buffer.size(); ++i)
p[i] = buffer[i];
p[buffer.size()] = '\0';
printf("%s\n", p);
}
delete[] p;
return 0;
}
测试:
12345
12345
12345678901234567890
12345678901234567890
12345678901234567890123456789012345678901234567890
12345678901234567890123456789012345678901234567890
^Z
如果读入字符串的长度大于动态分配的长度,就让动态分配内存的长度翻倍。
25
delete [] pa;
26
#include <iostream>
#include <memory>
#include <string>
using namespace std;
int main() {
const size_t MAX_SIZE = 10;
allocator<string> stringAlloc;
string * const p = stringAlloc.allocate(MAX_SIZE);
string *q = p;
string buffer;
while (cin >> buffer && q != p + MAX_SIZE) {
stringAlloc.construct(q++, buffer);
}
size_t size = q - p;
for (int i = 0; i < size; ++i)
cout << *(p + i) << endl;
while (q != p)
stringAlloc.destroy(--q);
return 0;
}
27
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
class QueryResult {
string target;
shared_ptr<vector<string>> spLines;
shared_ptr<set<int>> lineNumbers;
friend ostream& operator<< (ostream& os, const QueryResult& qr);
public:
QueryResult(string i_target, shared_ptr<vector<string>> i_spLines, shared_ptr<set<int>> i_lineNumbers)
: target(i_target), spLines(i_spLines), lineNumbers(i_lineNumbers) {}
};
class TextQuery {
shared_ptr<vector<string>> spLines;
shared_ptr<unordered_map<string, set<int>>> spWordToLine;
public:
explicit TextQuery(ifstream& infile) {
spLines = make_shared<vector<string>>();
spWordToLine = make_shared<unordered_map<string, set<int>>>();
string buffer, wordBuffer;
int lineCnt = 0;
while (getline(infile, buffer)) {
spLines->push_back(buffer);
istringstream iss(buffer);
while (iss >> wordBuffer) {
(*spWordToLine)[wordBuffer].insert(lineCnt);
}
++lineCnt;
}
}
QueryResult query(const string& target) {
shared_ptr<set<int>> spLineNumbers = make_shared<set<int>>((*spWordToLine)[target]);
return QueryResult(target, spLines, spLineNumbers);
}
};
ostream& operator<< (ostream& os, const QueryResult& qr) {
if (qr.lineNumbers->empty()) {
cout << qr.target << " occurs 0 times" << endl;
return os;
}
cout << qr.target << " occurs " << qr.lineNumbers->size() << " times" << endl;
for (const auto& lineNum : *(qr.lineNumbers)) {
cout << "(line " << lineNum + 1 << ") ";
cout << (*qr.spLines)[lineNum] << endl;
}
return os;
}
void runQueries(ifstream& infile) {
TextQuery tq(infile);
while (true) {
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
cout << tq.query(s) << endl;
}
}
int main() {
string filename("D:\\repos\\CppPrimerCh12\\CppPrimerCh12\\test.txt");
ifstream infile(filename);
assert(infile.is_open());
runQueries(infile);
return 0;
}
运行结果:
enter word to look for, or q to quit: I
I occurs 9 times
(line 1) Last week I went to the theatre.
(line 2) I had a very good seat. The play
(line 3) was very interesting. I did not
(line 6) They were talking loudly. I got
(line 7) very angry. I could not hear the
(line 8) actors. I turned round. I looked
(line 11) In the end, I could not bear it.
(line 12) I turned round again. 'I can't
(line 13) hear a word!' I said angrily. '
enter word to look for, or q to quit: week
week occurs 1 times
(line 1) Last week I went to the theatre.
enter word to look for, or q to quit: sdfadfa
sdfadfa occurs 0 times
enter word to look for, or q to quit: q
28
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
int main() {
// 打开文件
string filename("D:\\repos\\CppPrimerCh12\\CppPrimerCh12\\test.txt");
ifstream infile(filename);
assert(infile.is_open());
// 读入数据
vector<string> lines;
unordered_map<string, set<int>> wordToLine;
string buffer, wordBuffer;
int lineCnt = 0;
while (getline(infile, buffer)) {
lines.push_back(buffer);
istringstream iss(buffer);
while (iss >> wordBuffer) {
wordToLine[wordBuffer].insert(lineCnt);
}
++lineCnt;
}
// 处理查询
while (true) {
cout << "enter word to look for, or q to quit: ";
string target;
if (!(cin >> target) || target == "q")
break;
unordered_map<string, set<int>>::iterator it = wordToLine.find(target);
if (it == wordToLine.end()) {
cout << target << " occurs 0 times" << endl;
continue;
}
cout << target << " occurs " << it->second.size() << " times" << endl;
for (const auto& lineNum : it->second) {
cout << "(line " << lineNum + 1 << ") ";
cout << lines[lineNum] << endl;
}
}
return 0;
}
29
void runQueries(ifstream& infile) {
TextQuery tq(infile);
do {
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
cout << tq.query(s) << endl;
} while (true);
}
没区别吧,我只是单纯不习惯用 do-while
30
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
class QueryResult {
string target;
shared_ptr<vector<string>> spLines;
shared_ptr<set<int>> lineNumbers;
friend ostream& operator<< (ostream& os, const QueryResult& qr);
public:
QueryResult(string i_target, shared_ptr<vector<string>> i_spLines, shared_ptr<set<int>> i_lineNumbers)
: target(i_target), spLines(i_spLines), lineNumbers(i_lineNumbers) {}
};
class TextQuery {
shared_ptr<vector<string>> spLines;
shared_ptr<unordered_map<string, set<int>>> spWordToLine;
public:
explicit TextQuery(ifstream& infile) {
spLines = make_shared<vector<string>>();
spWordToLine = make_shared<unordered_map<string, set<int>>>();
string buffer, wordBuffer;
int lineCnt = 0;
while (getline(infile, buffer)) {
spLines->push_back(buffer);
istringstream iss(buffer);
while (iss >> wordBuffer) {
(*spWordToLine)[wordBuffer].insert(lineCnt);
}
++lineCnt;
}
}
QueryResult query(const string& target) {
shared_ptr<set<int>> spLineNumbers = make_shared<set<int>>((*spWordToLine)[target]);
return QueryResult(target, spLines, spLineNumbers);
}
};
ostream& operator<< (ostream& os, const QueryResult& qr) {
if (qr.lineNumbers->empty()) {
cout << qr.target << " occurs 0 times" << endl;
return os;
}
cout << qr.target << " occurs " << qr.lineNumbers->size() << " times" << endl;
for (const auto& lineNum : *(qr.lineNumbers)) {
cout << "(line " << lineNum + 1 << ") ";
cout << (*qr.spLines)[lineNum] << endl;
}
return os;
}
void runQueries(ifstream& infile) {
TextQuery tq(infile);
while (true) {
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
cout << tq.query(s) << endl;
}
}
int main() {
string filename("D:\\repos\\CppPrimerCh12\\CppPrimerCh12\\test.txt");
ifstream infile(filename);
assert(infile.is_open());
runQueries(infile);
return 0;
}
31
用 vector 的 push_back
方法也可以不断插入行号,但是相比 set 不会自动去重
32
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
class StrBlob {
public:
typedef vector<string>::size_type size_type;
StrBlob();
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
void push_back(const string& t) {
data->push_back(t);
}
void pop_back();
string& front();
const string& front() const;
string& back();
const string& back() const;
string& operator[] (const size_type index) {
check(index, "index out of range of StrBlob");
return (*data)[index];
}
const string& operator[] (const size_type index) const {
check(index, "index out of range of StrBlob");
return (*data)[index];
}
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string& msg) const;
};
StrBlob::StrBlob() : data(make_shared<vector<string>>()) {}
StrBlob::StrBlob(initializer_list<string> il) :
data(make_shared<vector<string>>(il)) {}
void StrBlob::check(size_type i, const string& msg) const {
if (i >= data->size())
throw std::out_of_range(msg);
}
string& StrBlob::front() {
check(0, "front on empty StrBlob");
return data->front();
}
const string& StrBlob::front() const {
check(0, "front on empty StrBlob");
return data->front();
}
string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
const string& StrBlob::back() const {
check(0, "back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
return data->pop_back();
}
class QueryResult {
string target;
StrBlob lines;
shared_ptr<set<int>> lineNumbers;
friend ostream& operator<< (ostream& os, const QueryResult& qr);
public:
QueryResult(string i_target, StrBlob i_lines, shared_ptr<set<int>> i_lineNumbers)
: target(i_target), lines(i_lines), lineNumbers(i_lineNumbers) {}
};
ostream& operator<< (ostream& os, const QueryResult& qr) {
if (qr.lineNumbers->empty()) {
cout << qr.target << " occurs 0 times" << endl;
return os;
}
cout << qr.target << " occurs " << qr.lineNumbers->size() << " times" << endl;
for (const auto& lineNum : *(qr.lineNumbers)) {
cout << "(line " << lineNum + 1 << ") ";
cout << qr.lines[lineNum] << endl;
}
return os;
}
class TextQuery {
StrBlob lines;
shared_ptr<unordered_map<string, set<int>>> spWordToLine;
public:
explicit TextQuery(ifstream& infile) {
spWordToLine = make_shared<unordered_map<string, set<int>>>();
string buffer, wordBuffer;
int lineCnt = 0;
while (getline(infile, buffer)) {
lines.push_back(buffer);
istringstream iss(buffer);
while (iss >> wordBuffer) {
(*spWordToLine)[wordBuffer].insert(lineCnt);
}
++lineCnt;
}
}
QueryResult query(const string& target) {
shared_ptr<set<int>> spLineNumbers = make_shared<set<int>>((*spWordToLine)[target]);
return QueryResult(target, lines, spLineNumbers);
}
};
void runQueries(ifstream& infile) {
TextQuery tq(infile);
while (true) {
cout << "enter word to look for, or q to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
cout << tq.query(s) << endl;
}
}
int main() {
string filename("D:\\repos\\CppPrimerCh12\\CppPrimerCh12\\test.txt");
ifstream infile(filename);
assert(infile.is_open());
runQueries(infile);
return 0;
}
33
#include <iostream>
#include <fstream>
#include <sstream>
#include <cassert>
#include <memory>
#include <string>
#include <set>
#include <unordered_map>
using namespace std;
class QueryResult {
friend ostream& operator<< (ostream& os, const QueryResult& qr);
private:
string target;
shared_ptr<vector<string>> spLines;
shared_ptr<set<int>> lineNumbers;
public:
QueryResult(string i_target, shared_ptr<vector<string>> i_spLines, shared_ptr<set<int>> i_lineNumbers)
: target(i_target), spLines(i_spLines), lineNumbers(i_lineNumbers) {}
set<int>::iterator begin() {
return lineNumbers->begin();
}
set<int>::iterator end() {
return lineNumbers->end();
}
shared_ptr<vector<string>> getFile() {
return spLines;
}
};
ostream& operator<< (ostream& os, const QueryResult& qr) {
if (qr.lineNumbers->empty()) {
cout << qr.target << " occurs 0 times" << endl;
return os;
}
cout << qr.target << " occurs " << qr.lineNumbers->size() << " times" << endl;
for (const auto& lineNum : *(qr.lineNumbers)) {
cout << "(line " << lineNum + 1 << ") ";
cout << (*qr.spLines)[lineNum] << endl;
}
return os;
}