C++ Primer笔记13---chapter12&13中的代码实例
Posted Ston.V
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ Primer笔记13---chapter12&13中的代码实例相关的知识,希望对你有一定的参考价值。
1.代码位置
有如下三处的代码实例:
1)P391 (一个单词转换的map)
2)P404(定义StrBlob类)& P420(核查指针类)
3)P430(文本查询程序)
2.一个单词转换的map
#include <iostream>
#include <map>
#include <fstream>
#include <sstream>
using namespace std;
/*
*有一个单词转换文件,内容如下:
* brb be right back
* k okay
* y why
* r are
* u you
* pic picture
* thk thacks
* 18r later
*
*希望转换的文本为:
* whrer r u
* y dont u send me a pic
* k thk 18r
*
*则程序应该输出:
* where are you
* why dont you send me a picture
* okay thanks later
*/
//1.首先建立映射,将转换表放进map容器
map<string, string> buildMap(ifstream &map_file){
map<string, string>trans_map; //保存转换规则,最终将返回他
string value,key;
while(map_file>>key){
//如果转换规则为空,就会导致这里的value实际上是下一个key;如果转换规则中有多个空格,这么写也会错误。
//注意第一个转换规则就有多个
//map_file>>value;
getline(map_file,value);
if(value.size() >1) //检查是否有转换规则
trans_map[key]=value.substr(1);
else
throw runtime_error("no rule for "+key);
}
return trans_map;
}
//2.对每个string进行转换
const string &s_transform(const string &s, const map<string, string>&m){
auto map_it=m.find(s);
if(map_it != m.cend()) //找到了
return map_it->second;
else
return s;
}
//3.依次读取每行输入文件,并进行替换
void word_transform(ifstream &map_file,ifstream &input){
auto trans_map = buildMap(map_file);
string text;
while(getline(input,text)){ //我们处理文件还是按行处理比较方便,按string处理会忽略空格和换行符
istringstream ss(text);
string word;
bool first_word=true; //每行第一个元素前不要加空格
while(ss >> word){
if(first_word)
first_word = false;
else
cout<<" ";
cout << s_transform(word,trans_map);
}
cout<<endl;
}
}
int main(){
ifstream trans_map("Test.txt");
ifstream input("input.txt");
if(trans_map && input)
//这里就能看出来,设计输入两个流的参数多舒服;我自己还想把流参数换为map参数
word_transform(trans_map,input);
else
cerr<< "opening file fails."<<endl;
return 0;
}
3.StrBlob & StrBlobPtr
StrBlob.h
这里太傻逼了:我原本以为声明友元,这里需要#include "StrBlobPtr.h";而在StrBlobPtr.h中由于使用了Blob也需要#include "StrBlob.h",这样就导致两个头文件相互包含,但是由于我们防止重复包含的宏存在,导致实际StrBlobPtr.h文件实际上并没有包含StrBlob.h,然后就出现了“&之前需要)”这种错误。实际上声明友元只用把类的声明前置即可。
https://blog.csdn.net/CSDNwei/article/details/50699298
#ifndef STRBLOB_H
#define STRBLOB_H
#include <vector>
#include <string>
#include <memory>
//#include "StrBlobPtr.h"
using namespace std;
/*
*Blob对象的不同拷贝之间共享相同的元素,即,当我们拷贝一个Blob时,原Blob对象及其拷贝应该应用相同的底层元素
*我们不能再Blob中直接保存vecotr,因为一个对象的成员在对象被销毁时也会被销毁,为了保证vecotr中的元素继续存在,需要将vector保存在动态内存中
*/
class StrBlobPtr;
class StrBlob
{
friend class StrBlobPtr;
//返回指向首元素和尾后元素的StrBlobPtr
/* StrBlobPtr begin(){return StrBlobPtr(*this);} //这里省略了默认实参0,实际返回了一个StrBlobPtr对象,其中curr为0
StrBlobPtr end(){
auto ret = StrBlobPtr(*this,data->size());
return ret;
}
*/
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob();
StrBlob(std::initializer_list<std::string> il);
virtual ~StrBlob();
size_type size() const {return data->size();}
bool empty() const { return data->empty();}
//添加和删除元素
void push_back(const std::string &t){data->push_back(t);}
void pop_back();
//元素访问
std::string &front();
std::string &back();
protected:
private:
std::shared_ptr<std::vector<std::string> >data;
//如果data[i]不合法,抛出一个异常
void check(size_type i, const std::string &msg)const;
};
#endif // STRBLOB_H
StrBlob.cpp
#include "StrBlob.h"
//我们使用make_shared来动态分配内存,即使这里的vector<string>我们并不知道长度也仍可以分配
StrBlob::StrBlob(): data(make_shared<std::vector<string> >()){}
StrBlob::StrBlob(std::initializer_list<std::string> il) : data(make_shared<std::vector<string> >(il)){}
StrBlob::~StrBlob()
{
//dtor
}
//pop_back和访问元素时都必须先check
void StrBlob::check(size_type i, const string &msg)const
{
if(i >= data->size())
throw out_of_range(msg);
}
string & StrBlob::front(){
check(0,"front on empty StrBlob");
return data->front();
}
string & StrBlob::back(){
check(0,"back on empty StrBlob");
return data->back();
}
void StrBlob::pop_back(){
check(0,"pop_back on empty StrBlob");
data->pop_back();
}
StrBlobPtr.h
#ifndef STRBLOBPTR_H
#define STRBLOBPTR_H
#include <vector>
#include <string>
#include <memory>
#include "StrBlob.h"
//保存一个weak_ptr,指向StrBlob中的data成员,通过weak_ptr不会改变指向的vector生存周期,但是可以阻止用户访问一个不再存在的vector的企图
class StrBlobPtr
{
public:
StrBlobPtr():curr(0){}
StrBlobPtr(StrBlob &a,size_t sz=0):wptr(a.data), curr(sz){}
std::string& deref() const;
StrBlobPtr& incr(); //前缀递增
virtual ~StrBlobPtr();
protected:
private:
//若检查成功,check返回一个指向vector的shared_ptr
std::shared_ptr<std::vector<std::string> > check(std::size_t, const std::string&) const;
//保存一个weak_ptr意味着底层vector可能会被销毁
std::weak_ptr<std::vector<std::string> >wptr;
//在数组当前的位置
std::size_t curr;
};
#endif // STRBLOBPTR_H
StrBlobPtr.cpp
#include "StrBlobPtr.h"
StrBlobPtr::~StrBlobPtr()
{
//dtor
}
std::shared_ptr<std::vector<std::string> >
StrBlobPtr::check(std::size_t i, const std::string& msg) const{
auto ret = wptr.lock(); //先check vector还在吗
if(!ret)
throw std::runtime_error("unbound StrBlobPtr");
if(i >= ret->size())
throw std::out_of_range(msg);
return ret;
}
std::string& StrBlobPtr::deref() const{
auto p = check(curr,"dereference past end");
return (*p)[curr]; //*p所指的对象是vector
}
StrBlobPtr& StrBlobPtr::incr(){
//如果curr已经指向尾后,就不能再递增了
check(curr, "increment past end of StrBlobPtr");
++curr;
return *this;
}
4.文本查询程序
转载:
头文件包含
1) 如果可以不包含头文件,那就不要包含,这时候前置声明可以解决问题,如果使用的仅仅是一个类的指针,没有使用这个类的具体对象(非指针),也没有访问到类的具体成员,那么前置声明就可以了,因为指针这一数据类型的大小是特定的,编译器可以获知.
2)尽量在CPP文件中包含头文件,而非在头文件中。
TextQuery.h
#ifndef TEXTQUERY_H
#define TEXTQUERY_H
#include <sstream>
#include <set>
#include <vector>
#include <string>
#include <memory>
#include <map>
/*
*这个类会处理源文件
*同时提供query操作,返回一个QueryResult对象
*/
class QueryResult;
class TextQuery
{
public:
TextQuery();
virtual ~TextQuery();
using line_no = std::vector<std::string>::size_type;
TextQuery(std::ifstream&);
QueryResult query(const std::string&) const;
protected:
private:
std::shared_ptr<std::vector<std::string> >file; //输入文件
std::map<std::string, std::shared_ptr<std::set<line_no>>> wm; //每个单词到他所在的行号的映射
};
#endif // TEXTQUERY_H
TextQuery.cpp
#include "TextQuery.h"
#include <fstream>
using namespace std;
TextQuery::TextQuery()
{
//ctor
}
TextQuery::TextQuery(ifstream &is): file(new vector<string>){
string text;
while(getline(is, text)){
file->push_back(text); //对文件中的每一行
int n= file->size()-1; //当前行号
istringstream line(text); //使用sstream来分解单词
string word;
while(line >> word){
//如果不在wm中,以之为下标在wm中添加一项
auto &lines = wm[word]; //lines是一个shared_ptr,指向行号的set集合
if(!lines) //在我们第一次遇到这个单词时,此指针为空
lines.reset(new set<line_no>); //分配一个新的set
lines->insert(n);
}
}
}
QueryResult TextQuery::query(const string& sought)const{
//如果找到sought,我们将返回一个指向此set的指针
static shared_ptr<set<line_no>> no_data(new set<line_no>);
auto loc = wm.find(sought);
if(loc == wm.end())
return QueryResult(sought, no_data, file);
else
return QueryResult(sought,loc->second,file);
}
TextQuery::~TextQuery()
{
//dtor
}
QueryResult.h
#ifndef QUERYRESULT_H
#define QUERYRESULT_H
#include <sstream>
#include <set>
#include <vector>
#include <string>
#include <memory>
class QueryResult
{
friend std::ostream &print(std::ostream&, const QueryResult&);
using line_no = std::vector<std::string>::size_type;
public:
QueryResult();
QueryResult(std::string s, std::shared_ptr<std::set<line_no>> p, std::shared_ptr<std::vector<std::string>> f):
sought(s), lines(p), file(f){}
virtual ~QueryResult();
protected:
private:
std::string sought; //查询单词
std::shared_ptr<std::set<line_no>>lines; //出现的行号
std::shared_ptr<std::vector<std::string>> file; //输入文件
};
#endif // QUERYRESULT_H
QueryResult.cpp
#include "QueryResult.h"
using namespace std;
QueryResult::QueryResult()
{
//ctor
}
QueryResult::~QueryResult()
{
//dtor
}
ostream & print(ostream &os, const QueryResult &qr){
//如果找到了单词,打印出次数和所有出现的位置
os << qr.sought << " occurs " << qr.lines->size() << ((qr.lines->size() > 1)?" times ":" time ") << endl;
for(auto num:*qr.lines)
os << "\\t(line " << num+1 << ") "<<*(qr.file->begin() +num) << endl;
return os;
}
以上是关于C++ Primer笔记13---chapter12&13中的代码实例的主要内容,如果未能解决你的问题,请参考以下文章
C++ Primer笔记16---chapter13 代码实例
C++ Primer笔记15---chapter13 拷贝控制2