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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值