Chapter12&Chapter13:程序实例
文本查询程序
要求:程序允许用户在一个给定文件中查询单词。查询结果是单词在文件中出现的次数及所在行的列表。如果一个单词在一行中出现多次,此行只列出一次。
对要求的分析:
1.读入文件,必须记住单词出现在每一行。因此,程序需要逐行读取文件,并将每一行分解成独立的单词;
2. 程序生成输出时,它必须能提取每个单词所关联的行号;行号必须按升序出现且无重复;必须能打印给定行号的文本。
数据结构分析:
使用vector<string>来保存整个文件的拷贝;使用set来保存每个单词出现的行号;使用map将单词与行号关联起来。
我们在此基础上,再抽象一层:
定义一个保存文本输入的类,叫TextQuery。有两个操作:一是读取文件构造对象;二是执行查询操作。
查询操作:返回单词所在行及其文本。此结果定义一个类:QueryResult。
TextQuery与QueryResult的关系
由于QueryResult所需要的数据都保存在一个QTextQuery对象中,我们必须确定如何访问它们。我们可以拷贝行号的set,但这样很耗时。而且我们不希望拷贝vector。
所以两个类共享了数据。
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<string, shared_ptr<set<line_no>>> wm;
}; TextQuery::TextQuery(ifstream& is)
:file(new vector<string>)
{
string text;
while (getline(is, text))
{
file->push_back(text);
int n = file->size() - ;
istringstream line(text);
string word;
while (line >> word)
{
auto &lines = wm[word];
if (!lines)
lines.reset(new set<line_no>);
lines->insert(n);
}
}
} class QueryResult
{
friend ostream& print(ostream&, const QueryResult&);
public:
using line_no = vector<string>::size_type;
QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<vector<string>> f)
:sought(s), lines(p), file(f) {}
private:
string sought;
shared_ptr<set<line_no>> lines;
shared_ptr<vector<string>> file;
}; //如果没有找到string,应该返回什么?
//我们定义一个局部static对象,它指向一个空行号set的shared_ptr,未找到单词,则返回此对象的一个拷贝
QueryResult TextQuery::query(const string &sought) const
{
static shared_ptr<set<line_no>> nodata(new set<line_no>);
//不使用下标运算符来查找,避免将单词添加到wm中
auto loc = wm.find(sought);
if (loc == wm.end())
return { sought, nodata, file };
else
return { sought, loc->second, file };
} ostream& print(ostream &os, const QueryResult &qr)
{
os << qr.sought << " occurs " << qr.lines->size() << " time(s)" << endl;
for (auto num : *qr.lines)
os << "\t(line " << num + << ") " << *(qr.file->begin() + num) << 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;
print(cout, tq.query(s)) << endl;
}
}
邮件与目录
资源管理并不是类需要定义自己的拷贝控制成员的唯一原因。一些类也需要拷贝控制成员的帮助来进行簿记工作或其他操作。
要求:有两个类Message和Folder,分别表示电子邮件消息和目录。每个Message可以出现在多个Folder中,Message内容只有一个副本——>如果一条Message的内容被改变,则我们从它所在的任何Folder来浏览此Message。
设计思路如下:
Message保存一个它所在Folder的指针的set;Folder保存一个它包含Message的指针的set。
当我们拷贝一个Message时,副本和原对象是不同的Message对象。因此,拷贝Message的操作包括消息内容和Folder指针set的拷贝;而且,我们必须在每个包含此消息的Folder中都添加一个指向新创建的Message的指针;
当我们销毁一个Message时,它将不复存在。因此,我们必须从包含此消息的所有Folder中删除指向此Message的指针;
当我们将一个Message对象赋予另一个Message对象时,左侧Message的内容会被右侧Message的内容所替代。同时,还必须更新Folder集合。
我们可以看到:
析构函数和拷贝赋值运算符都必须从包含一条Message的所有Folder中删除它。
拷贝构造函数和拷贝赋值运算符都要将一个Message添加到给定的一组Folder中。
我们定义两个private工具函数来完成这些工作。
class Message
{
friend class Folder;
friend void swap(Message&, Message&);
public:
explicit Message(const string &str = "")
:contents(str) {}
Message(const Message&);
Message& operator=(const Message&);
Message(Message &&m);
Message& Message::operator=(Message &&rhs);
~Message();
void save(Folder&);
void remove(Folder&);
private:
string contents;
set<Folder*> folders;
void addToFolders(const Message&);
void removeFromFolders();
void moveFolders(Message *m);
}; void Message::save(Folder &f)
{
folders.insert(&f);
f.addMsg(this);
} void Message::remove(Folder &f)
{
folders.erase(&f);
f.remMsg(this);
}
//拷贝控制:
void Message::addToFolders(const Message &m)
{
for (auto f : m.folders)
f->addMsg(this);
}
void Message::removeFromFolders()
{
for (auto f : folders)
f->remMsg(this);
} Message::Message(const Message &m)
:contents(m.contents),folders(m.folders)
{
addToFolders(m);
}
Message::~Message()
{
removeFromFolders();
}
Message& Message::operator=(const Message &rhs)
{
//先从左侧对象的folders中删除此Message指针,然后再添加到右侧运算对象的folders中,从而实现自赋值的正确处理
removeFromFolders();
contents = rhs.contents;
folders = rhs.folders;
addToFolders(rhs);
return *this;
}
//定义自己的swap版本
void swap(Message &lhs, Message &rhs)
{
using std::swap;
for (auto f : lhs.folders)
f->remMsg(&lhs);
for (auto f : rhs.folders)
f->remMsg(&rhs); swap(lhs.folders, rhs.folders);
swap(lhs.contents, rhs.contents);
for (auto f : lhs.folders)
f->addMsg(&lhs);
for (auto f : rhs.folders)
f->addMsg(&rhs);
} //移动操作
void Message::moveFolders(Message *m)
{
folders = std::move(m->folders);//使用set的移动赋值运算符
for (auto f : folders)
{
f->remMsg(m);
f->addMsg(this);
}
m->folders.clear();
} Message::Message(Message &&m)
:contents(std::move(m.contents))
{
moveFolders(&m);
} Message& Message::operator=(Message &&rhs)
{
if (this != &rhs)
{
removeFromFolders();
contents = std::move(rhs.contents);
moveFolders(&rhs);
}
return *this;
}
上述代码以Message类为例。
Chapter12&Chapter13:程序实例的更多相关文章
- (转)实战Memcached缓存系统(5)Memcached的CAS程序实例
1. 非CAS 首先看一个不是CAS的Memcached程序实例.实例的问题原型,见上一篇博文. 程序实例: package com.sinosuperman.memcached; import ja ...
- 如何让Windows程序只运行一个程序实例?
要实现VC++或者MFC只运行一个程序实例,一般采用互斥量来实现,即首先用互斥量封装一个只运行一个程序实例的函数接口: HANDLE hMutex = NULL; void MainDlg::RunS ...
- Linux Epoll介绍和程序实例
Linux Epoll介绍和程序实例 1. Epoll是何方神圣? Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6内核中正式引入,和select类似, ...
- 微信小程序实例源码大全
微信小程序实例源码大全下载 微信小应用示例代码(phodal/weapp-quick)源码链接:https://github.com/phodal/weapp-quick 微信小应用地图定位demo( ...
- MFC 只启动一个程序实例
问题描述: 我们开发过程中可能会经常遇到,只启动一个程序实例.即一个程序启动之后,如果再次执行该程序,将会恢复之前打开的程序,而不是打开一个新的程序. 实现原理:利用FindWindow/FindWi ...
- 一个完整的Installshield安装程序实例—艾泽拉斯之海洋女神出品(四) --高级设置二
原文:一个完整的Installshield安装程序实例-艾泽拉斯之海洋女神出品(四) --高级设置二 上一篇:一个完整的安装程序实例—艾泽拉斯之海洋女神出品(三) --高级设置一4. 根据用户选择的组 ...
- 微信小程序实例教程(一)
序言 开始开发应用号之前,先看看官方公布的「小程序」教程吧!(以下内容来自微信官方公布的「小程序」开发指南) 本文档将带你一步步创建完成一个微信小程序,并可以在手机上体验该小程序的实际效果.这个小程序 ...
- JDBC 程序实例小练习
JDBC 程序实例问题 编程实现如下功能:在数据库中建立一个表,表名为student,其结构为学号.姓名.性别.年龄.英语.JavaSE程序设计.初级日语.总分,在表中输入多条记录. 学生的总分信息, ...
- C++程序实例唯一方案,窗口只打开一次,程序只打开一次
首先是方法: // IsAlreadyRunning - 是否已经运行 BOOL IsAlreadyRunning() { BOOL bRet = FALSE; HANDLE hMutex = ::C ...
- 开发一个微信小程序实例教程
一.注册小程序账号 1.进入微信公众平台(https://mp.weixin.qq.com/),注册小程序账号,根据提示填写对应的信息即可.2.注册成功后进入首页,在 小程序发布流程->小程序开 ...
随机推荐
- 微信jssdk uploadImage 巨坑
//解决IOS无法上传的坑 if (localId.indexOf("wxlocalresource") != -1) { localId = localId.replace(&q ...
- Excel有用的宏
=Index({"同事","同学","亲戚"},b3) 前面的array默认索引从1开始. 如果b3为1.而枚举数组是: 0=>同事, ...
- 深入理解Java内存模型(三)——顺序一致性
数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java内存模型规范对数据竞争的定义如下: 在一个线程中写一个变量, 在另一个线程读同一个变量, 而且写和读没有通过同步来排序. 当代码 ...
- 用xshell操作linux系统的常用命令
(1)命令ls——列出文件 ls -la 给出当前目录下所有文件的一个长列表,包括以句点开头的“隐藏”文件 ls a* 列出当前目录下以字母a开头的所有文件 ls -l *.doc 给出当前目录下以. ...
- sql语句中能有中文 空格
EXEC dbo.usp_execute_sql_Prod 'SELECT * FROM dbo.QuanVerify_Log where ticketcode = ''3783665132'' ' ...
- 重新安装sqlyog时备份数据库链接列表的方法
一般在本机C:\Documents and Settings\Administrator\Application Data\SQLyog这个目录下有个sqlyog.ini文件.把这个文件备份一下就ok ...
- nginx.conf 配置文件详解
简单的实现nginx在前端做反向代理服务器的例子,处理js.png等静态文件,jsp等动态请求转发到其它服务器tomcat: user www www; worker_processes ; erro ...
- ACM - ICPC World Finals 2013 I Pirate Chest
原题下载:http://icpc.baylor.edu/download/worldfinals/problems/icpc2013.pdf 题目翻译: 问题描述 海盗Dick受够了在公海上厮杀.抢劫 ...
- Mac下配置idk
Mac下配置java #以下进入啰嗦模式演示添加jdk7 #下载jdk7的mac版 #官网下载地址http://www.oracle.com/technetwork/java/javase/downl ...
- CSS3与页面布局学习总结(三)——BFC、定位、浮动、垂直居中
一.BFC与IFC 1.1.BFC与IFC概要 BFC(Block Formatting Context)即“块级格式化上下文”, IFC(Inline Formatting Context)即行内格 ...