第 12 章 动态内存

练习 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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值