• Chapter12&Chapter13:程序实例


    文本查询程序

    要求:程序允许用户在一个给定文件中查询单词。查询结果是单词在文件中出现的次数及所在行的列表。如果一个单词在一行中出现多次,此行只列出一次。

    对要求的分析:

    1.读入文件,必须记住单词出现在每一行。因此,程序需要逐行读取文件,并将每一行分解成独立的单词;

    2. 程序生成输出时,它必须能提取每个单词所关联的行号;行号必须按升序出现且无重复;必须能打印给定行号的文本。

    数据结构分析:

    使用vector<string>来保存整个文件的拷贝;使用set来保存每个单词出现的行号;使用map将单词与行号关联起来。

    我们在此基础上,再抽象一层:

    定义一个保存文本输入的类,叫TextQuery。有两个操作:一是读取文件构造对象;二是执行查询操作。

    查询操作:返回单词所在行及其文本。此结果定义一个类:QueryResult。

    TextQuery与QueryResult的关系

    由于QueryResult所需要的数据都保存在一个QTextQuery对象中,我们必须确定如何访问它们。我们可以拷贝行号的set,但这样很耗时。而且我们不希望拷贝vector。

    所以两个类共享了数据

     1 class QueryResult;
     2 
     3 class TextQuery
     4 {
     5 public:
     6     using line_no = vector<string>::size_type;
     7     TextQuery(ifstream&);
     8     QueryResult query(const string&) const;//不完全类型可以声明(但不能定义)为参数类型和返回类型
     9 private:
    10     shared_ptr<vector<string>> file;
    11     map<string, shared_ptr<set<line_no>>> wm;
    12 };
    13 
    14 TextQuery::TextQuery(ifstream& is)
    15     :file(new vector<string>)
    16 {
    17     string text;
    18     while (getline(is, text))
    19     {
    20         file->push_back(text);
    21         int n = file->size() - 1;
    22         istringstream line(text);
    23         string word;
    24         while (line >> word)
    25         {
    26             auto &lines = wm[word];
    27             if (!lines)
    28                 lines.reset(new set<line_no>);
    29             lines->insert(n);
    30         }
    31     }
    32 }
    33 
    34 class QueryResult
    35 {
    36     friend ostream& print(ostream&, const QueryResult&);
    37 public:
    38     using line_no = vector<string>::size_type;
    39     QueryResult(string s, shared_ptr<set<line_no>> p, shared_ptr<vector<string>> f)
    40         :sought(s), lines(p), file(f) {}
    41 private:
    42     string sought;
    43     shared_ptr<set<line_no>> lines;
    44     shared_ptr<vector<string>> file;
    45 };
    46 
    47 //如果没有找到string,应该返回什么?
    48 //我们定义一个局部static对象,它指向一个空行号set的shared_ptr,未找到单词,则返回此对象的一个拷贝
    49 QueryResult TextQuery::query(const string &sought) const
    50 {
    51     static shared_ptr<set<line_no>> nodata(new set<line_no>);
    52     //不使用下标运算符来查找,避免将单词添加到wm中
    53     auto loc = wm.find(sought);
    54     if (loc == wm.end())
    55         return { sought, nodata, file };
    56     else
    57         return { sought, loc->second, file };
    58 }
    59 
    60 ostream& print(ostream &os, const QueryResult &qr)
    61 {
    62     os << qr.sought << " occurs " << qr.lines->size() << " time(s)" << endl;
    63     for (auto num : *qr.lines)
    64         os << "	(line " << num + 1 << ") " << *(qr.file->begin() + num) << endl;
    65     return os;
    66 }
    67 
    68 void runQueries(ifstream& infile)
    69 {
    70     TextQuery tq(infile);
    71     while (true)
    72     {
    73         cout << "enter word to look for, or q to quit: ";
    74         string s;
    75         if (!(cin >> s) || s == "q")
    76             break;
    77         print(cout, tq.query(s)) << endl;
    78     }
    79 }

    邮件与目录

    资源管理并不是类需要定义自己的拷贝控制成员的唯一原因。一些类也需要拷贝控制成员的帮助来进行簿记工作或其他操作。

    要求:有两个类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工具函数来完成这些工作。

      1 class Message
      2 {
      3     friend class Folder;
      4     friend void swap(Message&, Message&);
      5 public:
      6     explicit Message(const string &str = "")
      7         :contents(str) {}
      8     Message(const Message&);
      9     Message& operator=(const Message&);
     10     Message(Message &&m);
     11     Message& Message::operator=(Message &&rhs);
     12     ~Message();
     13     void save(Folder&);
     14     void remove(Folder&);
     15 private:
     16     string contents;
     17     set<Folder*> folders;
     18     void addToFolders(const Message&);
     19     void removeFromFolders();
     20     void moveFolders(Message *m);
     21 };
     22 
     23 void Message::save(Folder &f)
     24 {
     25     folders.insert(&f);
     26     f.addMsg(this);
     27 }
     28 
     29 void Message::remove(Folder &f)
     30 {
     31     folders.erase(&f);
     32     f.remMsg(this);
     33 }
     34 //拷贝控制:
     35 void Message::addToFolders(const Message &m)
     36 {
     37     for (auto f : m.folders)
     38         f->addMsg(this);
     39 }
     40 void Message::removeFromFolders()
     41 {
     42     for (auto f : folders)
     43         f->remMsg(this);
     44 }
     45 
     46 Message::Message(const Message &m)
     47     :contents(m.contents),folders(m.folders)
     48 {
     49     addToFolders(m);
     50 }
     51 Message::~Message()
     52 {
     53     removeFromFolders();
     54 }
     55 Message& Message::operator=(const Message &rhs)
     56 {
     57     //先从左侧对象的folders中删除此Message指针,然后再添加到右侧运算对象的folders中,从而实现自赋值的正确处理
     58     removeFromFolders();
     59     contents = rhs.contents;
     60     folders = rhs.folders;
     61     addToFolders(rhs);
     62     return *this;
     63 }
     64 //定义自己的swap版本
     65 void swap(Message &lhs, Message &rhs)
     66 {
     67     using std::swap;
     68     for (auto f : lhs.folders)
     69         f->remMsg(&lhs);
     70     for (auto f : rhs.folders)
     71         f->remMsg(&rhs);
     72 
     73     swap(lhs.folders, rhs.folders);
     74     swap(lhs.contents, rhs.contents);
     75     for (auto f : lhs.folders)
     76         f->addMsg(&lhs);
     77     for (auto f : rhs.folders)
     78         f->addMsg(&rhs);
     79 }
     80 
     81 //移动操作
     82 void Message::moveFolders(Message *m)
     83 {
     84     folders = std::move(m->folders);//使用set的移动赋值运算符
     85     for (auto f : folders)
     86     {
     87         f->remMsg(m);
     88         f->addMsg(this);
     89     }
     90     m->folders.clear();
     91 }
     92 
     93 Message::Message(Message &&m)
     94     :contents(std::move(m.contents))
     95 {
     96     moveFolders(&m);
     97 }
     98 
     99 Message& Message::operator=(Message &&rhs)
    100 {
    101     if (this != &rhs)
    102     {
    103         removeFromFolders();
    104         contents = std::move(rhs.contents);
    105         moveFolders(&rhs);
    106     }
    107     return *this;
    108 }

    上述代码以Message类为例。

  • 相关阅读:
    【UOJ#77】A+B Problem
    【AGC048B】Bracket Score
    ubuntu 下python opengl编程(2)
    网站建设的营销途径
    python脚本初探---新手写的QQ邮箱发送脚本
    Cstyle的C语言笔记 ---UEFI当中的面向对象模式
    date得到当前日期
    简单几步让SecureCRT更舒服【图文并茂】
    苹果的airplayer推荐
    【Cocos2d-X开发学习笔记】第22期:事件处理机制之触屏事件
  • 原文地址:https://www.cnblogs.com/wangyanphp/p/5838908.html
Copyright © 2020-2023  润新知