现在的位置: 首页 > 综合 > 正文

c++primer 例子文本程序再探

2019年05月18日 ⁄ 综合 ⁄ 共 8705字 ⁄ 字号 评论关闭

先附上前面的basket类的例子,里面很多思想也是我需要学的

#include <iostream>
#include <string>
#include <set>
#include <memory>

//买书的类
class quote
{
    friend double print_total(std::ostream &os, quote &q, std::size_t sz);
    public:
        quote() = default;
        quote(const std::string &book, double p):
            bookNo(book), price(p) { }
        quote(const quote &q):
            bookNo(q.bookNo), price(q.price) { }
        quote(quote &&q):             //一个右值拷贝
            bookNo(q.bookNo), price(q.price) { }
        quote& operator=(const quote &q)
        {
            bookNo = q.bookNo;
            price = q.price;
            return *this;
        }
        virtual double net_price(std::size_t sz)const
        {
            return price * sz;
        }
        const std::string& isbn()const
        {
            return bookNo;
        }
        //注意动态内存分配问题,基类和派生类的内存大小不同
        //所以我们写了个分配函数,且必须定义为虚函数。为了实现后面的动态分配
        //后面跟着成员限定符说明是用作左值还是右值
        virtual quote* clone()const&
        { return new quote(*this); }
        virtual quote* clone() &&
        { return new quote(std::move(*this)); }

    protected:
        double price;

    private:
        std::string bookNo;
};

double print_total(std::ostream &os, quote &q, std::size_t sz)
{
    double ret = q.net_price(sz);
    os << q.isbn() << " " << q.price << " " << ret << "\n";
    return ret;
}

//抽象基类,因为打折有许多不同的措施,但都基于discount和disc_num
//简单来说抽象出来一个标准,discount和disc_num 就可以说是一个标准。
class disc_quote : public quote
{
    public:
        disc_quote() = default;
        disc_quote(const std::string &book, double p, double disc, std::size_t num):
            quote(book, p), discount(disc), disc_num(num) { }
        double net_price(std::size_t sz)const = 0;   //纯虚函数

    protected:
        double discount;
        std::size_t disc_num;

    private:
};


//第一种打折方式
class one_bulk_quote : public disc_quote
{
    public:
        using disc_quote::disc_quote;
        double net_price(std::size_t sz)const override
        {
            if(sz > disc_num)
            {
                return sz * price * discount;
            }
            else
            {
                return sz * price;
            }
        }
        //对第一种打折类实现动态分配
        one_bulk_quote* clone()const &
        {
            return new one_bulk_quote(*this);
        }
        one_bulk_quote* clone() && 
        {
            return new one_bulk_quote(std::move(*this));
        }

    protected:

    private:
};

class two_bulk_quote : public disc_quote
{
    public:
        using disc_quote::disc_quote;
        double net_price(std::size_t sz)
        {
            if(sz > disc_num)
            {
                return (sz-disc_num) * price * discount + disc_num * price;
            }
            else
            {
                return sz * price;
            }
        }

    protected:

    private:
};

class basket
{
    public:
        //void add_item(const std::shared_ptr<quote>&sale)
        //{
        //    items.insert(sale);
        //}
        //如果像上面那样写分配内存不会是动态决定的,参数传递是quote,那么无论传参是什么分配都是quote,如果是派生类就会被砍掉一部分。
        
        void add_item(const quote &sale)
        {
            items.insert(std::shared_ptr<quote>(sale.clone()));
        }
        void add_item(quote && sale)
        {
            items.insert(std::shared_ptr<quote>(std::move(sale).clone()));
        }
        double total_receipt(std::ostream &os)const;

    protected:

    private:
        //自定义比较函数
        static bool compare(const std::shared_ptr<quote> &lhs, const std::shared_ptr<quote> &rhs)
        {
            return lhs->isbn() < rhs->isbn();
        }
        std::multiset<std::shared_ptr<quote>, decltype(compare)*> items{compare};//decltype不能推断出指针和引用
};

double basket::total_receipt(std::ostream &os)const
{
    double sum = 0.0;
    //注意下upper_bound的作用
    for(auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter))
    {
        sum += print_total(std::cout, **iter, items.count(*iter));
    }
    os << "total Sale: " << sum << std::endl;
    return sum;
}

int main()
{

}

文本程序

#ifndef _TEXTQUERY_H_
#define _TEXTQUERY_H_

#include <iostream>
#include <map>
#include <set>
#include <fstream>
#include <sstream>
#include <memory>
#include <vector>
#include <string>
#include <algorithm>

class QueryResult;
class TextQuery;

using line_no = std::vector<std::string>::size_type;

class QueryResult
{
    friend class TextQuery;
    friend std::ostream& operator<<(std::ostream &os, const QueryResult &q);
    public:
        QueryResult() = default;
        QueryResult(std::string s, std::shared_ptr<std::set<line_no>>li, std::shared_ptr<std::vector<std::string>>wf):
            q_word(s), lines(li), wordfile(wf) { }
        std::set<line_no>::iterator begin();
        std::set<line_no>::iterator end();
        std::shared_ptr<std::vector<std::string>> get_file();

    private:
        std::string q_word;                                 //要查询单词
        std::shared_ptr<std::set<line_no>>lines;            //出现行号
        std::shared_ptr<std::vector<std::string>>wordfile;  //单词文件
};

class TextQuery
{
    friend class QueryResult;
    public:
        TextQuery() = default;
        TextQuery(std::ifstream &is);
        QueryResult query(const std::string &s)const;

    private:
        std::shared_ptr<std::vector<std::string>>m_file;
        std::map<std::string, std::shared_ptr<std::set<line_no>>>m_word_line;     //考虑共享数据的需求
    
};

  
//Query_base 抽象基类,制定一个标准
class Query_base
{
    friend class Query;
    protected:
        virtual ~Query_base() = default;

    private:
        //
        virtual QueryResult eval(const TextQuery &)const = 0;
        virtual std::string rep()const = 0;
};

//接口类,基类包括派生类都可以通过Query来使用,隐藏实现
class Query
{
    friend Query operator~(const Query &);
    friend Query operator&(const Query &, const Query &);
    friend Query operator|(const Query &, const Query &);
    friend std::ostream& operator<<(std::ostream &os, const Query &query);
    public:
        Query(const std::string&);//wait WordQuery
        //Query是Query_base的唯一接口,所以必须定义纯虚函数
        //且Query就可以通过指针来调用派生类各自Query_base虚函数
        QueryResult eval(const TextQuery &t)const
        { return q->eval(t); }
        std::string rep()const
        { return q->rep(); }
    private:
        //构造函数定义为private,不希望一般用户随便定义Query_base对象。
        Query(std::shared_ptr<Query_base> query):q(query) { }
        //通过智能指针实现动态绑定
        std::shared_ptr<Query_base> q;
};

//派生类
class WordQuery: public Query_base
{
    friend class Query;
    WordQuery(const std::string &s):query_word(s) { }
    //具体的类
    QueryResult eval(const TextQuery &t)const
    { return t.query(query_word); }
    std::string rep()const { return query_word; }
    std::string query_word;        //要查找的单词
};

//无论哪种查询都是建立在WordQuery的根本上,So Query的构造函数用一个WordQuery来初始化
inline Query::Query(const std::string &s):
    q(new WordQuery(s)) { }

class NotQuery: public Query_base
{
    friend Query operator~(const Query &);
    NotQuery(const Query &q):query(q) { }
    //具体的类,覆盖掉纯虚函数
    std::string rep()const { return "~(" + query.rep() + ")"; }
    QueryResult eval(const TextQuery &)const;
    Query query;
};

inline Query operator~(const Query &operand)
{
    //注意返回值
    return std::shared_ptr<Query_base>(new NotQuery(operand));
}

//两个运算符的抽象基类
class BinaryQuery: public Query_base
{
    protected:
    BinaryQuery(const Query &l, const Query &r, std::string s):
        lhs(l), rhs(r), opSym(s) { }
    std::string rep()const 
    { return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")"; }
    //eval函数没有定义,继承了纯虚函数,还是抽象基类

    Query lhs, rhs;
    std::string opSym;
};

class AndQuery: public BinaryQuery
{
    friend Query operator&(const Query&, const Query&);
    AndQuery(const Query &left, const Query &right):
        BinaryQuery(left, right, "&") { }
    //具体的类,覆盖了eval并且继承了rep
    QueryResult eval(const TextQuery&) const;
};

inline Query operator&(const Query&lhs, const Query&rhs)
{
    return std::shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}

class OrQuery: public BinaryQuery
{
    friend Query operator|(const Query&, const Query&);
    OrQuery(const Query &left, const Query &right):
        BinaryQuery(left, right, "|") { }
    //具体的类,覆盖了eval并且继承了rep
    QueryResult eval(const TextQuery&)const;
};

inline Query operator|(const Query&lhs, const Query&rhs)
{
    return std::shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}

#include "textquery.h"

std::ostream& operator<<(std::ostream &os, const QueryResult &q)
{
    os << "element occurs:" << q.lines->size() << " times\n";
    for(line_no i : *q.lines)
    {
        os << "   " << "line(" << i+1 << ") ";
        os << *(q.wordfile->begin()+i) << "\n";
        //os << (*(q.wordfile))[i] << "\n";   //注意运算符优先级
    }
    return os;
}

TextQuery::TextQuery(std::ifstream &is):m_file(new std::vector<std::string>)
{
    std::string word;
    std::string line;
    static int i = 0;
    while(std::getline(is, line))
    {
            //行号
        m_file->push_back(line);
        i = m_file->size()-1;
        std::istringstream ist(line);
        while(ist >> word)
        {
            auto &li = m_word_line[word];  //返回set的shared_ptr
            if(!li)
                li.reset(new std::set<line_no>);
            li->insert(i);
        }
    }
}

QueryResult TextQuery::query(const std::string &s)const
{
    static std::shared_ptr<std::set<line_no>> nodata(new std::set<line_no>);
    auto ret = m_word_line.find(s);
    if(ret == m_word_line.end())
        return QueryResult(s, nodata, m_file);
    else
        return QueryResult(s, ret->second, m_file);
}

std::ostream& operator<<(std::ostream &os, const Query &query)
{
    //query.rep()内部还是通过指针来调用的,是需调用
    return os << query.rep();
}

//QueryResult
std::set<line_no>::iterator QueryResult::begin()
{
    return lines->begin();
}

std::set<line_no>::iterator QueryResult::end()
{
    return lines->end();
}

std::shared_ptr<std::vector<std::string>> QueryResult::get_file()
{
    return wordfile;
}


//eval

QueryResult
OrQuery::eval(const TextQuery &text)const
{
    auto right = rhs.eval(text), left = lhs.eval(text);
    auto ret_lines = std::make_shared<std::set<line_no>>(left.begin(), left.end());
    ret_lines->insert(right.begin(), right.end());

    return QueryResult(rep(), ret_lines, left.get_file());
}

QueryResult
AndQuery::eval(const TextQuery &text)const
{
    auto left = lhs.eval(text), right = rhs.eval(text);
    auto ret_lines = std::make_shared<std::set<line_no>>();
    set_intersection(left.begin(), left.end(), right.begin(), right.end(), inserter(*ret_lines, ret_lines->begin()));

    return QueryResult(rep(), ret_lines, left.get_file());
}

QueryResult
NotQuery::eval(const TextQuery &text)const
{
    auto result = query.eval(text);
    auto ret_lines = std::make_shared<std::set<line_no>>();
    auto beg = result.begin(), end = result.end();
    auto sz = result.get_file()->size();
    //相当于两个集合两个指针一起走。
    for(std::size_t n = 0; n != sz; ++n)
    {
        if(beg == end || *beg != n)
            ret_lines->insert(n);
        else if(beg != end)
            ++beg;
    }

    return QueryResult(rep(), ret_lines, result.get_file());
}

#include "textquery.h"

int main(int argc, char *argv[])
{
    std::ifstream is(argv[1]);
    TextQuery t1(is);
    std::string word;
    Query q = Query("fiery") & Query("bird") | Query("wind");
    //Query q = Query("fiery");
    //Query q = Query("A");
    std::cout << q << std::endl;
    std::cout << q.eval(t1) << std::endl;
}

运行结果:

这个例子自己也看了挺久,感觉还是有点复杂,可能是自己写的这种面向对象的不多吧,还要多加联系。

这个例子中的很多思想非常好,值得我们去学习。

总结:

1.好好体会抽象基类纯虚函数这个概念,在c++面向对象编程是非常重要的。

抱歉!评论已关闭.