练习 12.1:
b1和b2共享一个StrBlob对象,b2被销毁,但b2中的元素没有被销毁,所以他们都有4个元素;
练习 12.2:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<initializer_list>
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;
private:
shared_ptr<vector<string>> data;
// 如果data[i]不合法,抛出一个异常
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 out_of_range(msg);
}
void StrBlob::pop_back()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
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();
}
练习 12.3:
可以添加const版本的push_back和pop_back,但似乎没有合乎逻辑的理由。编译器不会报错,因为它不会修改data(指针)而是修改data所指的值,所以使用const指针是完全合法的。通过不添加const成员函数,然后声明一个const StrBlob对象,最后发现无法调用push_back()和pop_back()。而添加const成员函数,则可以对const StrBlob对象调用push_back()和pop_back()。
练习 12.4:
因为i的类型是vector<string>::size_type是一个unsigned。当传入任何小于0的参数时,它将转换成一个大于0的数。
练习 12.5:
关键字explicit防止从initializer_list自动转换为StrBlob。这种设计选择易于使用,但难于调试。
练习 12.6:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
using namespace std;
vector<int>* dynamic_vector_generator()
{
vector<int>* ptr_v = new vector<int>();
return ptr_v;
}
void dynamic_vector_processor(vector<int>* ptr_v)
{
int i;
while (cin >> i)
ptr_v->push_back(i);
}
void dynamic_vector_printer(vector<int>* ptr_v)
{
for (const auto& i : *ptr_v)
cout << i << " ";
cout << endl;
}
int main()
{
vector<int>* ptr_v = dynamic_vector_generator();
dynamic_vector_processor(ptr_v);
dynamic_vector_printer(ptr_v);
delete ptr_v;
return 0;
}
练习 12.7:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
using namespace std;
shared_ptr<vector<int>> dynamic_vector_generator()
{
shared_ptr<vector<int>> sptr_v = make_shared<vector<int>>();
return sptr_v;
}
void dynamic_vector_processor(shared_ptr<vector<int>> sptr_v)
{
int i;
while (cin >> i)
sptr_v->push_back(i);
}
void dynamic_vector_printer(shared_ptr<vector<int>> sptr_v)
{
for (const auto& i : *sptr_v)
cout << i << " ";
cout << endl;
}
int main()
{
shared_ptr<vector<int>> sptr_v = dynamic_vector_generator();
dynamic_vector_processor(sptr_v);
dynamic_vector_printer(sptr_v);
return 0;
}
练习 12.8:
#include<iostream>
#include<memory>
using namespace std;
// 通过new返回的指针值来区分内存分配成功或失败——
// 成功返回一个合法指针,转换为整型是一个非零值,可转换为bool值true;
// 分配失败,p得到nullptr,其整型值是0,可转换为bool值false.
bool b() {
int* p = new int();
// ...
return p;
}
int main()
{
cout << boolalpha << b() << endl;
//delete p; // p转换为bool类型,已分配的动态内存无法被释放。因此,会发生内存泄漏。
return 0;
}
练习 12.9:
int *q = new int(42), *r = new int(100);
r = q; // 发生了内存泄漏。因为在r = q执行之后,没有指针指向r曾经指向的int类型。
// 它意味着没有机会为它释放内存。
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);
r2 = q2; // 自动释放r2原来指向的对象。因为在r2 = q2执行之后,递增q2指向对象的引用计数,
// 递减r2原来指向的对象的引用计数
练习 12.10:
此调用是正确的,利用p创建一个临时的shared_ptr赋予了process的参数ptr,p和ptr都指向相同的int对象,引用计数被正确地置为2。process执行完毕后,ptr被销毁,引用计数减1。
#include<iostream>
#include<memory>
using namespace std;
void process(std::shared_ptr<int> ptr)
{
cout << "use_count:" << ptr.use_count() << endl;
}
int main()
{
shared_ptr<int> p(new int(42));
process(shared_ptr<int>(p));
cout << "use_count:" << p.use_count() << endl;
return 0;
}
练习 12.11:
此调用是错误的,使用get返回一个内置指针,不能将一个内置指针隐式转换为一个智能指针。用内置指针初始化一个临时的智能指针,临时的智能指针在函数结束后内存被释放,此时p变成空悬指针,使用p时将发生未定义行为。
练习 12.12:
a)合法,将shared_ptr中的sp复制到process(),引用计数为2,结束后引用计数递减为1。
b)不合法,普通指针不能隐式转换为智能指针,只能进行直接初始化。
c)不合法,普通指针不能隐式转换为智能指针。
d)合法,但是错误的程序。p是一个指向int对象的普通指针,被用来创建一个临时shared_ptr,
传递给process的参数ptr,引用计数为1。当process执行完毕,ptr被销毁,引用计数变为0,int对象被销毁,p变为空悬指针。
练习 12.13:
p的内存被释放,所以接下来试图使用sp的时候会发生未定义行为。使用get返回的指针的代码不能delete此指针。
练习 12.14:
#include<iostream>
#include<string>
#include<memory>
using namespace std;
struct destination // 表示我们正在连接什么
{
destination(string _ip, int _port): ip(_ip), port(_port){}
string ip;
int port;
};
struct connection // 使用连接所需的信息
{
connection(string _ip, int _port) : ip(_ip), port(_port) {}
string ip;
int port;
};
connection connect(destination* pDest) // 打开连接
{
shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port));
cout << "creating connection(" << pConn.use_count() << ")" << endl;
return *pConn;
}
void disconnect(connection Conn) // 关闭给定连接
{
cout << "connection close(" << Conn.ip << ":" << Conn.port << ")" << endl;
}
void end_connection(connection* p) // 删除器
{
disconnect(*p);
}
void f(destination& d)
{
connection c = connect(&d);
shared_ptr<connection> p(&c, end_connection);
cout << "connecting now(" << p.use_count() << ")" << endl;
}
int main()
{
destination d("202.118.176.67", 3316);
f(d);
return 0;
}
练习 12.15:
#include<iostream>
#include<string>
#include<memory>
using namespace std;
struct destination // 表示我们正在连接什么
{
destination(string _ip, int _port): ip(_ip), port(_port){}
string ip;
int port;
};
struct connection // 使用连接所需的信息
{
connection(string _ip, int _port) : ip(_ip), port(_port) {}
string ip;
int port;
};
connection connect(destination* pDest) // 打开连接
{
shared_ptr<connection> pConn(new connection(pDest->ip, pDest->port));
cout << "creating connection(" << pConn.use_count() << ")" << endl;
return *pConn;
}
void disconnect(connection Conn)
{
cout << "connection close(" << Conn.ip << ":" << Conn.port << ")" << endl;
}
void f(destination& d)
{
connection c = connect(&d);
shared_ptr<connection> p(&c, [](connection* p) {disconnect(*p); });
cout << "connecting now(" << p.use_count() << ")" << endl;
}
int main()
{
destination d("202.118.176.67", 3316);
f(d);
return 0;
}
练习 12.16:
#include<iostream>
#include<string>
#include<memory>
using namespace std;
int main()
{
unique_ptr<string> p1(new string("Cpp"));
// unique_ptr不支持普通的拷贝或赋值操作
unique_ptr<string> p2(p1);
unique_ptr<string> p3 = p1;
return 0;
}
练习 12.17:
(a) 不合法。无法将int转换为unique_ptr<int>的指针。
(b) 不合法。可以编译,但会在运行时导致错误。p1销毁时会释放此该对象,但该对象不是使用new分配的,其行为是未定义的。
(c) 不合法。可以编译,但会在运行时导致错误。unique_ptr不支持拷贝,unique_ptr将释放原始指针所指向的对象。
(d) 不合法。存在与(b)相同的问题。
(e) 合法。定义unique_ptr时,将其绑定到一个new返回的指针,初始化unique_ptr必须采用直接吃实话形式
(f) 不合法。但用p2管理的对象的地址来初始化p5,造成两个unique_ptr指向相同的内存地址。当其中一个unique_ptr被销毁(或调用reset释放对象)时,该内存被释放,另一个unique_ptr变为空悬指针。
练习 12.18:
share_ptr是共享的智能指针,允许多个智能指针共享同一对象,其支持普通的拷贝和赋值操作,无需使用release来返回指针并置空指针。
练习 12.19 && 练习 12.20:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<fstream>
using namespace std;
class StrBlobPtr;
// StrBlob类
class StrBlob
{
public:
typedef vector<string>::size_type size_type;
friend class StrBlobPtr;
// 使用StrBlobPtr的构造函数返回begin和end操作
StrBlobPtr begin();
StrBlobPtr end();
StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob(initializer_list<string> il) :
data(make_shared<vector<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()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
// 元素访问
string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
const string& front()const
{
check(0, "front on empty StrBlob");
return data->front();
}
string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const string& back()const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
shared_ptr<vector<string>> data;
// 如果data[i]不合法,抛出一个异常
void check(size_type i, const string& msg)const
{
if (i >= data->size())
throw out_of_range(msg);
}
};
class StrBlobPtr
{
public:
StrBlobPtr(): curr(0) {}
StrBlobPtr(StrBlob& a, size_t sz = 0): wptr(a.data), curr(sz) {}
bool operator!=(const StrBlobPtr& p) { return p.curr != curr; }
string& deref()const; // 解引用
StrBlobPtr& incr(); // 前缀递增
private:
// 若检查成功,check返回一个指向vector的shared_ptr
shared_ptr<vector<string>> check(size_t, const string&)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(); // vector是否还存在
if (!ret)
throw runtime_error("unbound StrBlobPtr");
if (i >= ret->size()) // 索引值是否合法
throw out_of_range(msg);
return ret; // 否则返回这项vector的shared_ptr
}
string& StrBlobPtr::deref()const // 解引用
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
StrBlobPtr& StrBlobPtr::incr() // 前缀递增
{
// 如果curr已经指向容器的尾后位置,就不能递增它
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
StrBlobPtr StrBlob::begin() { return StrBlobPtr(*this); }
StrBlobPtr StrBlob::end() { return StrBlobPtr(*this, data->size()); }
int main()
{
ifstream ifs("data.txt");
StrBlob sb;
string str;
while (getline(ifs, str))
sb.push_back(str);
for (StrBlobPtr pbeg(sb.begin()), pend(sb.end()); pbeg != pend; pbeg.incr())
cout << pbeg.deref() << endl;
return 0;
}
练习 12.21:
原始版本更好。因为它可读性更强。
练习 12.22:
#include<iostream>
#include<string>
#include<vector>
#include<memory>
#include<fstream>
using namespace std;
class ConstStrBlobPtr;
// StrBlob类
class StrBlob
{
public:
typedef vector<string>::size_type size_type;
friend class ConstStrBlobPtr;
ConstStrBlobPtr begin()const; // add const
ConstStrBlobPtr end()const; // add const
StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob(initializer_list<string> il) :
data(make_shared<vector<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()
{
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
string& front()
{
check(0, "front on empty StrBlob");
return data->front();
}
const string& front()const
{
check(0, "front on empty StrBlob");
return data->front();
}
string& back()
{
check(0, "back on empty StrBlob");
return data->back();
}
const string& back()const
{
check(0, "back on empty StrBlob");
return data->back();
}
private:
shared_ptr<vector<string>> data;
void check(size_type i, const string& msg)const
{
if (i >= data->size())
throw out_of_range(msg);
}
};
class ConstStrBlobPtr
{
public:
ConstStrBlobPtr(): curr(0) {}
ConstStrBlobPtr(const StrBlob& a, size_t sz = 0): wptr(a.data), curr(sz) {}
bool operator!=(const ConstStrBlobPtr& p) { return p.curr != curr; }
const string& deref()const
{
auto p = check(curr, "dereference past end");
return (*p)[curr];
}
ConstStrBlobPtr& incr()
{
check(curr, "increment past end of ConstStrBlobPtr");
++curr;
return *this;
}
private:
shared_ptr<vector<string>> check(size_t i, const string& msg)const
{
auto ret = wptr.lock();
if (!ret)
throw runtime_error("unbound ConstStrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret;
}
weak_ptr<vector<string>> wptr;
size_t curr;
};
ConstStrBlobPtr StrBlob::begin()const { return ConstStrBlobPtr(*this); }
ConstStrBlobPtr StrBlob::end()const { return ConstStrBlobPtr(*this, data->size()); }
练习 12.23:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main()
{
// 使用字符串字面常量
const char* c1 = "Hello ";
const char* c2 = "Cpp";
unsigned len = strlen(c1) + strlen(c2) + 1;
char* c = new char[len]();
strcat_s(c, len, c1);
strcat_s(c, len, c2);
cout << c << endl;
// 使用string对象
string s1 = "Hello ";
string s2 = "Cpp";
strcpy_s(c, len, (s1 + s2).c_str());
cout << c << endl;
delete [] c;
return 0;
}
练习 12.24:
#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main()
{
// 手动分配数组长度
cout << "How long do you want the string? ";
int size = 0;
cin >> size;
char* input = new char[size + 1]();
cout << "input the string: ";
cin >> input;
cout << input << endl;
delete[] input;
// 自动分配数组长度
cout << "input the string: ";
for (string str; cin >> str; cout << "input the string: ")
{
char* input = new char[str.size() + 1]();
strcpy_s(input, str.size() + 1, str.c_str());
cout << input << endl;
delete[] input;
}
return 0;
}
练习 12.25:
delete [] pa;
练习 12.26:
#include<iostream>
#include<string>
#include<memory>
using namespace std;
void string_by_allocator(int n)
{
allocator<string> alloc;
auto const p = alloc.allocate(n); // 分配内存
auto q = p;
for (string s; cin >> s && q != p + n; ++q)
alloc.construct(q, s); // 构造元素
while (q != p)
{
cout << *(--q) << " ";
alloc.destroy(q); // 执行析构函数
}
alloc.deallocate(p, n); // 释放内存
}
int main()
{
string_by_allocator(6);
return 0;
}
练习 12.27:
#include<iostream>
#include<string>
#include<fstream>
#include<memory>
#include<vector>
#include<set>
#include<map>
#include<sstream>
#include<algorithm>
using namespace std;
class QueryResult;
class TextQuery
{
public:
using line_no = vector<string>::size_type;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<vector<string>> file; // 输入文件
// 每个单词到它所在的行号的集合的map
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using line_no = vector<string>::size_type;
QueryResult(string s, shared_ptr<set<line_no>> plines, shared_ptr<vector<string>> f):
sought(s), lines(plines), file(f) { }
private:
string sought; // 查询单词
shared_ptr<set<line_no>> lines; // 出现的行号
shared_ptr<vector<string>> file; // 输入文件
};
TextQuery::TextQuery(ifstream& ifs) : file(new vector<string>)
{
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file->push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
auto& lines = wm[word];
if (!lines) // 第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>);
lines->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& sought)const
{
// 如果未找到sought, 我们将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc != wm.end())
return QueryResult(sought, loc->second, file);
else
return QueryResult(sought, nodata, file);
}
// 打印结果
ostream& print(ostream& os, const QueryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size()
<< (qr.lines->size() > 1 ? " times" : " time") << endl;
// 打印单词出现的每一行
for (auto line : *qr.lines)
os << "\t(line " << line + 1 << ") " << (*qr.file)[line] << endl;
return os;
}
void runQueries(ifstream& infile)
{
TextQuery tq(infile); // 保存文件并建立查询map
while (true)
{
cout << "enter word to look for, or \"q\" to quit: ";
string s;
if (cin >> s && s != "q")
print(cout, tq.query(s)) << endl;
else
break;
}
}
练习 12.28:
#include<iostream>
#include<string>
#include<fstream>
#include<vector>
#include<set>
#include<map>
#include<sstream>
#include<algorithm>
using namespace std;
int main()
{
ifstream ifs("letter.txt");
vector<string> file;
using line_no = vector<string>::size_type;
map<string, set<line_no>> wm;
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file.push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
wm[word].insert(lineNo);
}
}
while (true)
{
cout << "enter word to look for, or \"q\" to quit: ";
string s;
if (!(cin >> s) || s == "q")
break;
auto sought = wm.find(s);
if (sought != wm.end())
{
cout << s << " occurs " << sought->second.size()
<< (sought->second.size() > 1 ? " times" : " time") << endl;
for (auto line : sought->second)
cout << "\t(line " << line + 1 << ") " << file[line] << endl;
}
else
cout << s << " occurs 0 time" << endl;
}
return 0;
}
练习 12.29:
do {
cout << "enter word to look for, or \"q\" to quit: ";
string s;
if (cin >> s && s != "q")
print(cout, tq.query(s)) << endl;
else
break;
} while ( true );
练习 12.30:
#include<iostream>
#include<string>
#include<fstream>
#include<memory>
#include<vector>
#include<set>
#include<map>
#include<sstream>
#include<algorithm>
using namespace std;
class QueryResult;
class TextQuery
{
public:
using line_no = vector<string>::size_type;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<vector<string>> file; // 输入文件
// 每个单词到它所在的行号的集合的map
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using line_no = vector<string>::size_type;
QueryResult(string s, shared_ptr<set<line_no>> plines, shared_ptr<vector<string>> f):
sought(s), lines(plines), file(f) { }
private:
string sought; // 查询单词
shared_ptr<set<line_no>> lines; // 出现的行号
shared_ptr<vector<string>> file; // 输入文件
};
TextQuery::TextQuery(ifstream& ifs) : file(new vector<string>)
{
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file->push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
auto& lines = wm[word];
if (!lines) // 第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>);
lines->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& sought)const
{
// 如果未找到sought, 我们将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc != wm.end())
return QueryResult(sought, loc->second, file);
else
return QueryResult(sought, nodata, file);
}
// 打印结果
ostream& print(ostream& os, const QueryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size()
<< (qr.lines->size() > 1 ? " times" : " time") << endl;
// 打印单词出现的每一行
for (auto line : *qr.lines)
os << "\t(line " << line + 1 << ") " << (*qr.file)[line] << endl;
return os;
}
void runQueries(ifstream& infile)
{
TextQuery tq(infile); // 保存文件并建立查询map
while (true)
{
cout << "enter word to look for, or \"q\" to quit: ";
string s;
if (cin >> s && s != "q")
print(cout, tq.query(s)) << endl;
else
break;
}
}
int main()
{
ifstream ifs("letter.txt");
runQueries(ifs);
return 0;
}
练习 12.31:
vector不能确保没有重复。因此,就这个程序而言,set是一个更好的选择。
练习 12.32:
#include"ex12_22.h"
#include<iostream>
#include<fstream>
#include<map>
#include<set>
class QueryResult;
class TextQuery
{
public:
using line_no = StrBlob::size_type;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<StrBlob> file; // 输入文件
// 每个单词到它所在的行号的集合的map
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using line_no = StrBlob::size_type;
QueryResult(string s, shared_ptr<set<line_no>> plines, shared_ptr<StrBlob> f) :
sought(s), lines(plines), file(f) { }
private:
string sought; // 查询单词
shared_ptr<set<line_no>> lines; // 出现的行号
shared_ptr<StrBlob> file; // 输入文件
};
TextQuery::TextQuery(ifstream& ifs) : file(new StrBlob)
{
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file->push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
auto& lines = wm[word];
if (!lines) // 第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>);
lines->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& sought)const
{
// 如果未找到sought, 我们将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc != wm.end())
return QueryResult(sought, loc->second, file);
else
return QueryResult(sought, nodata, file);
}
// 打印结果
ostream& print(ostream& os, const QueryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size()
<< (qr.lines->size() > 1 ? " times" : " time") << endl;
// 打印单词出现的每一行
for (auto line : *qr.lines)
{
ConstStrBlobPtr p(*qr.file, line);
os << "\t(line " << line + 1 << ") " << p.deref() << endl;
}
return os;
}
void runQueries(ifstream& infile)
{
TextQuery tq(infile); // 保存文件并建立查询map
while (true)
{
cout << "enter word to look for, or \"q\" to quit: ";
string s;
if (cin >> s && s != "q")
print(cout, tq.query(s)) << endl;
else
break;
}
}
int main()
{
ifstream ifs("storyDataFile.txt");
runQueries(ifs);
return 0;
}
练习 12.33:
#include"ex12_22.h"
#include<iostream>
#include<fstream>
#include<map>
#include<set>
class QueryResult;
class TextQuery
{
public:
using line_no = StrBlob::size_type;
TextQuery(ifstream&);
QueryResult query(const string&)const;
private:
shared_ptr<StrBlob> file; // 输入文件
// 每个单词到它所在的行号的集合的map
map<string, shared_ptr<set<line_no>>> wm;
};
class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using ResultIter = set<StrBlob::size_type>::iterator;
using line_no = StrBlob::size_type;
QueryResult(string s, shared_ptr<set<line_no>> plines, shared_ptr<StrBlob> f) :
sought(s), lines(plines), file(f) { }
ResultIter begin() const { return lines->begin(); }
ResultIter end() const { return lines->end(); }
shared_ptr<StrBlob> get_file() const { return file; }
private:
string sought; // 查询单词
shared_ptr<set<line_no>> lines; // 出现的行号
shared_ptr<StrBlob> file; // 输入文件
};
TextQuery::TextQuery(ifstream& ifs) : file(new StrBlob)
{
line_no lineNo = 0;
for (string line; getline(ifs, line); ++lineNo)
{
file->push_back(line); // 保存此行文本
istringstream line_iss(line);
for (string word; line_iss >> word;)
{
// 避免读入标点符号
auto punct_pos = find_if(word.begin(), word.end(), ispunct);
if (punct_pos != word.end())
word = string(word.begin(), punct_pos);
// 如果单词不在wm中,以之为下标在wm中添加一项
auto& lines = wm[word];
if (!lines) // 第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>);
lines->insert(lineNo);
}
}
}
QueryResult TextQuery::query(const string& sought)const
{
// 如果未找到sought, 我们将返回指向此set的指针
static shared_ptr<set<line_no>> nodata(new set<line_no>);
auto loc = wm.find(sought);
if (loc != wm.end())
return QueryResult(sought, loc->second, file);
else
return QueryResult(sought, nodata, file);
}
// 打印结果
ostream& print(ostream& os, const QueryResult& qr)
{
os << qr.sought << " occurs " << qr.lines->size()
<< (qr.lines->size() > 1 ? " times" : " time") << endl;
// 打印单词出现的每一行
for (auto line : *qr.lines)
{
ConstStrBlobPtr p(*qr.file, line);
os << "\t(line " << line + 1 << ") " << p.deref() << endl;
}
return os;
}
void runQueries(ifstream& infile)
{
TextQuery tq(infile); // 保存文件并建立查询map
while (true)
{
cout << "enter word to look for, or \"q\" to quit: ";
string s;
if (cin >> s && s != "q")
print(cout, tq.query(s)) << endl;
else
break;
}
}
int main()
{
ifstream ifs("storyDataFile.txt");
runQueries(ifs);
return 0;
}