C++ Primer 中文第 5 版练习答案 第 12 章 动态内存

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

因为 ivector<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值