C++primer-学习心得-15-面向对象程序设计

31 篇文章 3 订阅

C++primer-学习心得-15-面向对象程序设计

15.1 OOP概述

OOP(object-oriented programming,面向对象程序设计)的核心思想是数据抽象、继承和动态绑定。

数据抽象–将类的接口与实现分离;继承–定义相似的类型并对其相似关系建模;动态绑定–一定程度上忽略相似类型的区别而以统一的方式使用他们的对象。

继承

通过继承联系在一起的类构成一种层次关系。层次关系的根部有一个基类,其他类则直接或间接地从基类继承而来,这些继承得到的类称为派生类。

对于某些函数基类希望它的派生类各自定义适合自己的版本,此时基类就将这些函数声明成虚函数。如我们将基类quote编写成:

class quote{
    public:
    std::string isbn()const;
    virtual double net_price(std::size_t)const;
}

派生类必须通过使用类派生列表明确指出它是从哪个/哪些基类继承而来的。类派生列表的形式为:冒号后面接着以逗号分隔的基类列表,每个基类前可以由访问说明符。

class Bulk_quote:public quote{
    public:
    double net_price(std::size_t)const override;
}

通过在函数形参列表后面添加关键字overrice来显式指明这个成员函数改写基类的虚函数。

动态绑定

动态绑定能用同一段代码分别处理quote和Bulk_quote的对象。

double price_total(ostream &os,const quote &item,size_t n){
    double ret=item.net_price(n);
    os<<"ISBN:"<<item.isbn()<<" # sold:"<<n<<"total due: "<<ret<<endl;
    return ret;
}

根据传入的对象来决定执行的哪个版本的net_price函数

//basic的类型是quote,bulk的类型是Bulk_quote
price_total(cout,basic,20);//调用quote的net_price
price_total(cout,bulk,20);//调用Bulk_quote的net_price

动态绑定又称为运行时绑定。

15.2 定义基类和派生类

1.定义基类

C++中,基类需要区分两种成员函数:基类希望其派生类进行覆盖的函数、基类希望派生类直接继承而不要修改的函数。前者通常被定义为虚函数,当我们使用指针或者引用调用序函数时,调用将被动态绑定–根据引用或指针绑定的对象的不同,可能调用基类的版本也可能调用派生类的版本。

基类通过再其成员函数声明前加上关键字virtual使该函数执行动态绑定。关键字virtual只能出现在类内部的声明语句之前,不能出现在类外部的函数定义。在基类中一个函数声明为虚函数,在派生类中这个函数也隐式的是虚函数。

某些情况下基类希望其中的某些成员能被它的派生类访问而不能被其他用户访问,我们用受保护的访问运算符protected来说明这样的成员。

2.定义派生类

派生类通过类派生列表指出它从那个基类继承而来。形式为:冒号后面紧跟以逗号分隔的基类列表。

类派生列表中每个基类前可以有三种访问说明符中的一种:publicprivateprotected。访问说明符的作用是控制派生类从基类继承而来的成员是否对派生类的用户可见。

派生类通常会覆盖它继承的虚函数,如果派生类没有覆盖基类中的某个虚函数,派生类就会直接继承基类中的版本。

派生类可以在它覆盖的函数前使用关键字virtual,但不是必要的。C++11允许派生类通过添加关键字override来显示的注明用这个函数来覆盖它继承的函数。

由于在派生类中含有它基类的对用的组成部分,所以我们可以把派生类的对象当成基类的对象来使用,我们也可以将基类的指针或引用绑定到派生类对象中的基类部分上。如

Quote item;			//基类对象
Bulk_quote bule;	//派生类对象
Quote *p=&item;
p=&bulk;
Quote &r=bulk;

这种转换通常称为派生类到基类的类型转换。

派生类并不能直接初始化从基类继承而来的成员,派生类必须用基类的构造函数来初始化这些成员。

派生类可以访问基类的公有成员和受保护成员。

如果基类定义了一个静态成员,则在整个继承体系中都只存在该成员的唯一定义。

派生类的声明中只包含类名,不包含它的派生列表。

一个类要想作为基类,那么这个类必须是已经定义的而不是仅仅声明的。这样的话一个类要派生自它自己就是不允许的。

一个类可以同时是基类和派生类。

一个类如果我们不希望它被其它类继承,可以在定义类时在类名之后添加关键字final来防止继承的发生。

class NoDerived final{...}

3.类型转换与继承

前面有提到,我们可以将基类的指针或引用绑定到派生类对象上,而这有一层极为重要的含义:当使用基类的指针或引用时,我们其实并不清楚绑定对象的真实类型。

我们在使用存在继承关系的类型时,必须要将一个变量或表达式的静态类型与该表达式的动态类型区分开来。动态类型直到运行时才可知。

由于一个基类的对象可能时派生类的一部分,也可能不是,所以不存在从基类向派生类的自动类型转换。

类型转换仅限于指针或引用类型,对象之间时不存在类型转换的。


如,一个简单的基类Quote.h

#pragma once
//按原价销售的书
#include <string>
class Quote
{
public:
	Quote(std::string s,double d):id(s),price(d){}
	~Quote(){};
	std::string isbn()const { return id; };
	virtual double net_price(std::size_t n)const { return n * price; };
protected:
	std::string id;//书籍isbn号
	unsigned saled;//销售数量
	double price;//书的单价
};

一个派生类BulkQuote.h

#pragma once
#include "Quote.h"
class BulkQuote :
    public Quote
{
public:
	BulkQuote(std::string s,double d):Quote(s,d){}//利用基类的构造函数初始化从基类继承的成员
	double net_price(std::size_t n) const override;
};

double BulkQuote::net_price(std::size_t n) const	//覆盖从基类继承的虚函数
{
	double ret = n * price;
	if (n > 10)
		ret = 0.8 * 10.0 + (n - 10) * price;
	else
		ret *= 0.8;
	return ret;
}

动态绑定示例,main.cpp

#include "Quote.h"
#include "BulkQuote.h"
#include <iostream>
#include <string>
double printTotal(std::ostream &os,const Quote&item,size_t n)
{
	double ret = item.net_price(n);
	os << "ISBN:" << item.isbn() << " # sold: " << n << " total due: " << ret << std::endl;
	return ret;
}

int main()
{
	Quote quote("123456", 12.6);
	BulkQuote bulk_quote("2290027812", 43.96);
	printTotal(std::cout, quote, 20);
	printTotal(std::cout, bulk_quote, 22);

	return 0;
}

输出

ISBN:123456 # sold: 20 total due: 252
ISBN:2290027812 # sold: 22 total due: 535.52

C:\Users\xhh\Source\Repos\cppPrimer15_1\Debug\cppPrimer15_1.exe (进程 86876)已退出,代码为 0。
按任意键关闭此窗口. . .

15.3 虚函数

注意:对虚函数的调用在运行时才被解析。

前面我们提到可以用关键字override来说明派生类中的虚函数,这样做的一个好处是可以让编译器为我们检查一些错误,如形参列表是否一致。

另外,我们可以将某个函数指定为final,这样之后任何尝试覆盖该函数的操作都将引发错误。

我们也可以在对虚函数的调用时不进行动态绑定,指定执行虚函数的特定版本,做法是使用作用域运算符。如:

double ret = item.Quote::net_price(n);

15.4 抽象基类

纯虚函数:和普通的虚函数不同,纯虚函数无须定义,通过在函数体的位置加上=0就可以将一个虚函数说明为一个纯虚函数。

double net_price(std::size_t) const=0;

含有纯虚函数的类是抽象基类。抽象基类负责定义接口,而其它后续的类可以覆盖该接口。我们不能创建一个抽象基类的对象。

15.5 访问控制与继承

如前面提到的,一个类用protected来声明哪些它希望与派生类分享但不想被其它公共访问使用的成员。也就是对于这个类的派生类来说这些受保护成员是private的,不能直接访问,但对于派生类来说,这些成员仿佛在类的内部的private下。protected可以看作是publicprivate中和后的产物。

受保护的成员对于派生类的成员和友元来说都是可以访问的,但成员和友元只能通过派生类的对象来访问基类的受保护成员。

某个类的成员对其继承来的成员的访问权限收到两个因素的影响:基类中成员的访问说明符、派生类的派生列表中的访问说明符。

派生类访问说明符的目的是控制派生类用户对于基类成员的访问权限。

如果是public则成员遵循原有的访问说明符。如果是private则继承的成员也都是private的则类的用户不可访问继承的成员。如果是protected则派生类的用户不能访问继承的成员,但派生类的成员和友元可以访问那些继承的成员。

另外,只有公有的继承,用户代码才能使用派生类向基类的转换,如果是受保护的或私有的则不行。但不管以什么方式继承,派生类的成员和友元都能使用派生类向基类的转换。如果继承是共有的或受保护的,那么派生类的派生类的成员和友元可使用派生类向基类的转换,否则不行。

友元关系不能传递也不能继承。

可以用using来改变个别成员的访问级别。

默认的继承保护级别:默认情况下,class定义的派生类是私有继承,struct定义的派生类是公有继承。

15.6 继承中的类作用域

每个类定义自己作用域,当存在继承关系时,派生类的作用域嵌套在其基类的作用域之内。如果一个名字在派生类的作用域内无法解析,则编译器将继续在外层的基类中寻找该名字的含义。

声明在内层作用域的函数不会重载声明在外层的作用域的函数,所以定义在派生类的函数不会重载其基类中的成员。如果派生类中的成员和基类的成员同名,则派生类将在其作用域内隐藏其基类成员,即使形参不一致。

如果派生类希望基类的一个虚函数的所有的重载版本都可见,那么它要么覆盖所有版本,要么一个也不覆盖,如果只想覆盖个别版本,又想所有版本都可见,就必须要使用一条using声明语句了。

15.7 构造函数与拷贝控制

继承体系中的类有时也需要控制当其对象执行一些包括创建、拷贝、移动、赋值和销毁在内的操作时发生什么样的行为。

15.7.1 虚析构函数

继承体系中重要的一点是,基类中通常应该定义一个虚析构函数,这样我们就能够动态分配继承体系中的对象了。

virtual ~Quote()=default;

这样的作用是当我们delete基类指针时将运行正确的虚构函数版版本。

15.7.2 合成拷贝控制和赋值

如果基类中缺少移动操作会阻止派生类拥有自己的合成移动操作,所以当我们需要执行移动操作时应该首先在基类中定义。

class Quote{
public:
    Quote()=default;
    Quote(const Quote&)=default;
    Quote(Quote&&)=default;
    Quote& operator=(const Quote&)=default;
    Quote& operator=(Quote&&)=default;
    virtual ~Quote()=default;
}

15.7.3 派生类的拷贝控制成员

为派生类定义拷贝或移动构造函数时,通常使用对应的基类构造器初始化对象的基类部分。

class Base{...};
class D:public Base{
public:
    D(const D& d):Base(d){...}
    D(D&& d):Base(std::move(d)){...}
    D& operator=(const D &rhs){Base::operator=(rhs);
                               return *this; }
    ~D(){...};
}

15.7.4 继承的构造函数

15.8 容器与继承

由于我们不能在容器中存放不同类型的元素,如果我们要存放继承体系中的对象时,需要采取间接存储的方式。即存放指向基类的指针,最好是选择智能指针。

vector<shared_ptr<Quote>>basket;
basket.push_back(make_shared<Quote>("0-201-82470-1",50));
basket.push_back(make_shared<Bulk_quote>("0-201-54848-8",50,10,25));
cout<<basket.back()->net_price(15)<<endl;

如果我们又想用一个容器存放具有继承关系的类的对象,又不想直接使用存放指针的容器,那么我们就只能自己编写一个容器类Basket,这个类的内部是用存放指针的容器,但通过封装使得外部表象为可以直接添加具有继承关系的类的对象。本身实现起来并不困难,但其中有一些需要注意的点,有些体现了C++的OOP的精髓。下面是一个示例:

基类Quote.h

#pragma once
//按原价销售的书
#include <string>
class Quote
{
public:
	Quote(std::string s,double d):id(s),price(d){}
	~Quote(){};
	std::string isbn()const { return id; };
	virtual double net_price(std::size_t n)const { return n * price; };
	virtual Quote* clone()const&{return new Quote(*this);}
	virtual Quote* clone() &&{return new Quote(std::move(*this));}
protected:
	std::string id;//书籍isbn号
	unsigned saled;//销售数量
	double price;//书的单价
};

继承类BulkQuote.h

#pragma once
#include "Quote.h"
class BulkQuote :
    public Quote
{
public:
	BulkQuote(std::string s,double d):Quote(s,d){}//利用基类的构造函数初始化从基类继承的成员
	double net_price(std::size_t n) const override;
	BulkQuote* clone() const &{return new BulkQuote(*this);}
	BulkQuote* clone() &&{return new BulkQuote(std::move(*this));}
};

double BulkQuote::net_price(std::size_t n) const	//覆盖从基类继承的虚函数
{
	double ret = n * price;
	if (n > 10)
		ret = 0.8 * 10.0 + (n - 10) * price;
	else
		ret *= 0.8;
	return ret;
}

自定义的容器Busket.h

#pragma once
#include "Quote.h"
#include <memory>
#include <set>
#include <iostream>
class Basket
{
public:
	void add_item(const std::shared_ptr<Quote>&sale){items.insert(sale);};
	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 &)const;
private:
	//用于比较元素,一些关联容器需要用到
	static bool compare(const std::shared_ptr<Quote>&lhs,const std::shared_ptr<Quote>&rhs)
	{
		return lhs->isbn()<rhs->isbn();
	}//multiset保存多个报价,按照compare成员排序
	std::multiset<std::shared_ptr<Quote>,decltype(compare)*>items{compare};
};
double Basket::total_receipt(std::ostream &os) const
{
	double sum=0.0;
	for (auto iter=items.cbegin();iter!=items.cend();++iter)
	{
		sum+=(**iter).net_price(items.count(*iter));
		os << "ISBN:" << (**iter).isbn() << " # sold: " << items.count(*iter) << " total due: " << (**iter).net_price(items.count(*iter)) << std::endl;
	}
	os<<"Total Sale: "<<sum<<std::endl;
	return sum;
}

使用这个类,main.cpp

#include "Quote.h"
#include "BulkQuote.h"
#include <iostream>
#include <string>
#include "Basket.h"
double printTotal(std::ostream &os,const Quote&item,size_t n)
{
	double ret = item.net_price(n);
	os << "ISBN:" << item.isbn() << " # sold: " << n << " total due: " << ret << std::endl;
	return ret;
}

int main()
{
	Quote quote("123456", 12.6);
	BulkQuote bulk_quote("2290027812", 43.96);
	Basket ba;
	ba.add_item(quote);
	ba.add_item(bulk_quote);
	ba.add_item(Quote("123123",22));
	ba.add_item(BulkQuote("xiehh",33));
	ba.total_receipt(std::cout);
	
	return 0;
}

输出

ISBN:123123 # sold: 1 total due: 22
ISBN:123456 # sold: 1 total due: 12.6
ISBN:2290027812 # sold: 1 total due: 35.168
ISBN:xiehh # sold: 1 total due: 26.4
Total Sale: 96.168

C:\Users\xhh\Source\Repos\cppPrimer15_1\Debug\cppPrimer15_1.exe (进程 73896)已退出,代码为 0。
按任意键关闭此窗口. . .

15.9 文本查询程序再探

这个问题书上给出了具体的解决方案,给出了一个框架,但没有给出完整的代码,所以一些细节需要我们自己去把握。尽管有了明确的方向,但要准确的实现这个问题还是存在一定的难度的。下面我给出我的解决这一问题的答案作为参考,虽然还不太完美,但能够切实的解决这个问题。

QueryResult.h

#pragma once
#include <set>
#include <vector>

class AndQuery;
class OrQuery;
class WordQuery;
class NotQuery;
//class QueryBase;
class QueryResult
{
	friend WordQuery;
	friend NotQuery;
	friend OrQuery;
	friend AndQuery;
	friend std::ostream& print(std::ostream &os,const  QueryResult &qr);
public:
	QueryResult(std::string s,std::shared_ptr<std::set<unsigned>>l,std::shared_ptr<std::vector<std::string>>f):
	sought(s),lines(l),file(f){}
	QueryResult& operator=(const QueryResult &r)
	{
		this->sought=r.sought;
		this->lines=r.lines;
		this->file=r.file;
		return *this;
	}
private:
	std::string sought;
	std::shared_ptr<std::set<unsigned>>lines;
	std::shared_ptr<std::vector<std::string>>file;
};

TextQuery.h

#pragma once
#include <vector>
#include <map>
#include <fstream>//定义了读写命名文件的类型
#include <iostream>
#include <sstream>//定义了读写内存string对象的类型
class TextQuery
{
public :
	using line_no=std::vector<std::string>::size_type;
	TextQuery(std::ifstream&);
	QueryResult query(const std::string&)const;
	QueryResult noQuery(const std::string&)const;
	QueryResult orQuery(const std::string&,const std::string &)const;
	QueryResult andQuery(const std::string&,const std::string &)const;
private:
	std::shared_ptr<std::vector<std::string>>file;
	std::map<std::string,std::shared_ptr<std::set<unsigned>>>wm;
};
TextQuery::TextQuery(std::ifstream &is):file(new std::vector<std::string>)
{
	std::string text;
	while(std::getline(is,text))
	{
		file->push_back(text);
		int n=file->size()-1;
		std::istringstream line(text);
		std::string word="";
		for(char const c:text)
		{
			if(std::isalpha(c))
				word.push_back(c);
			else if(!word.empty())
			{
				auto &lines=wm[word];
				if(!lines)
					lines.reset(new std::set<unsigned>);
				lines->insert(n);
				word="";
			}
		}
		/*
		while(line>>word)
		{
			auto &lines=wm[word];
			if(!lines)
				lines.reset(new std::set<unsigned>);
			lines->insert(n);
		}*/
	}
}
inline QueryResult TextQuery::query(const std::string &sought) const
{
	static std::shared_ptr<std::set<unsigned>>nodata(new std::set<unsigned>);
	auto loc=wm.find(sought);
	if(loc==wm.end())
		return QueryResult(sought,nodata,file);
	else
		return QueryResult(sought,loc->second,file);
}
inline QueryResult TextQuery::noQuery(const std::string &sought) const
{
	static std::shared_ptr<std::set<unsigned>>nodata(new std::set<unsigned>);
	auto loc=wm.find(sought);
	std::shared_ptr<std::set<unsigned>>ls(new std::set<unsigned>);
	if(loc==wm.end())
		return QueryResult(sought,nodata,file);
	else
	{
		for(size_t i=0;i<file->size();++i)
		{
			if(loc->second->find(i)==loc->second->end())
				ls->insert(i);
		}
		return QueryResult(sought,ls,file);
	}
}
inline QueryResult TextQuery::orQuery(const std::string &l, const std::string &r) const
{
	static std::shared_ptr<std::set<unsigned>>nodata(new std::set<unsigned>);
	std::string strs=l;
	strs.append(" or ");
	strs.append(r);
	auto locl=wm.find(l);
	auto locr=wm.find(r);
	std::shared_ptr<std::set<unsigned>>ls(new std::set<unsigned>);
	if(locl==wm.end()&&locr==wm.end())
		return QueryResult(l,nodata,file);
	else
	{
		for(size_t i=0;i<file->size();++i)
		{
			if(locl->second->find(i)!=locl->second->end() || locr->second->find(i)!=locr->second->end())
				ls->insert(i);
		}
		return QueryResult(strs,ls,file);
	}
}
inline QueryResult TextQuery::andQuery(const std::string &l, const std::string &r) const
{
	static std::shared_ptr<std::set<unsigned>>nodata(new std::set<unsigned>);
	std::string strs=l;
	strs.append(" and ");
	strs.append(r);
	auto locl=wm.find(l);
	auto locr=wm.find(r);
	//auto ls=locl->second;
	std::shared_ptr<std::set<unsigned>>ls(new std::set<unsigned>);
	//ls->clear();
	if(locl==wm.end()||locr==wm.end())
		return QueryResult(l,nodata,file);
	for(auto const c:*(locr->second))
	{
		if(locl->second->find(c)!=locl->second->end())
			ls->insert(c);
	}
	return QueryResult(strs,ls,file);
	
}

接口类Query.h

#pragma once
//接口类,隐藏了整个继承体系
#include <functional>
#include <string>
#include <memory>
#include "QueryBase.h"
#include "QueryResult.h"
#include "TextQuery.h"

class Query
{
	friend Query operator~(const Query &);
	friend Query operator |(const Query&,const Query&);
	friend Query operator &(const Query&,const Query&);
public:
	Query(const std::string &);
	QueryResult eval(const TextQuery &t)const{return q->eval(t);}
	std::string rep(const QueryResult&qr)const {return q->rep(qr);}
	std::string getstr() const{return q->getstr();}
	Query& operator=(const Query&r)
	{
		this->q=r.q;
		return *this;
	}
private:
	Query(std::shared_ptr<QueryBase>query):q(query){}
	std::shared_ptr<QueryBase>q;
};

Query::Query(const std::string &s):q(std::make_shared<WordQuery>(WordQuery(s))){}

Query operator~(const Query &r)
{
	NotQuery n(r.getstr());
	std::shared_ptr<QueryBase>const query(std::make_shared<NotQuery>(n));
	Query rc(r.getstr());
	rc.q=query;
	return rc;
}
inline Query operator|(const Query &l, const Query &r)
{
	OrQuery o(l.getstr(),r.getstr());
	std::shared_ptr<QueryBase>const query(std::make_shared<OrQuery>(o));
	Query rc(r.getstr());
	rc.q=query;
	return rc;
}
inline Query operator&(const Query &l, const Query &r)
{
	AndQuery a(l.getstr(),r.getstr());
	std::shared_ptr<QueryBase>const query(std::make_shared<AndQuery>(a));
	Query rc(r.getstr());
	rc.q=query;
	return rc;
}

QueryBase.h

#pragma once
#include <string>

#include "QueryResult.h"

#include "TextQuery.h"
#include <sstream>
//抽象基类
class QueryBase
{
	friend class Query;
protected:
	using line_no=TextQuery::line_no;
	virtual ~QueryBase()=default;
private:
	//返回当前Query匹配的QueryText
	virtual QueryResult eval(const TextQuery&)const=0;//纯虚函数
	//查询的一个string
	virtual std::string rep(const QueryResult &)const=0;//纯虚函数
	std::string str;
	virtual std::string getstr()=0;
};
//查找一个给定的单词
class WordQuery:public QueryBase
{
public:
	WordQuery(std::string s):str(s){}
private:
	QueryResult eval(const TextQuery&t) const override
	{
		
		return t.query(str);
	}
	std::string rep(const QueryResult& qr) const override
	{
		std::ostringstream os;
		os<<qr.sought<<" -occurs "<<qr.lines->size()<<" times."<< std::endl;
		for(auto num: *qr.lines)
			os<<"\t(line"<<num+1<<")"<<*(qr.file->begin()+num)<< std::endl;
		return os.str();
	}
	std::string str;
	std::string getstr() override{return str;};
};
//查找给定的单词没有出现的行的集合
class NotQuery:public QueryBase
{
public:
	NotQuery(std::string s):str(s){}
private:
	QueryResult eval(const TextQuery&t) const override
	{
		return t.noQuery(str);
	};
	std::string rep(const QueryResult& qr) const override
	{
		std::ostringstream os;
		os<<qr.sought<<" -not occurs "<<qr.lines->size()<<" times."<< std::endl;
		for(auto num: *qr.lines)
			os<<"\t(line"<<num+1<<")"<<*(qr.file->begin()+num)<< std::endl;
		return os.str();
	};
	std::string str;
	std::string getstr() override{return str;};

};
//两个运算对象的查询
class BinaryQuery:public QueryBase
{
private:
	QueryResult eval(const TextQuery&t) const override{return t.query(" ");};
	std::string rep(const QueryResult&qr) const override{return "test";};
	std::string getstr() override{return " ";};

};
//两个运算对象分别出现的行的并集
class OrQuery:public BinaryQuery
{
public:
	OrQuery(std::string s1,std::string s2):str1(s1),str2(s2){}
private:
	QueryResult eval(const TextQuery&t) const override
	{
		return t.orQuery(str1,str2);
	};
	std::string rep(const QueryResult&qr) const override
	{
		std::ostringstream os;
		os<<qr.sought<<" -occurs "<<qr.lines->size()<<" times."<< std::endl;
		for(auto num: *qr.lines)
			os<<"\t(line"<<num+1<<")"<<*(qr.file->begin()+num)<< std::endl;
		return os.str();
	};
	std::string str1,str2;
	std::string getstr() override{return str1;};

};
//两个运算对象分别出现的行的交集
class AndQuery:public BinaryQuery
{
public:
	AndQuery(std::string s1,std::string s2):str1(s1),str2(s2){}
private:
	QueryResult eval(const TextQuery&t) const override
	{
		return t.andQuery(str1,str2);
	};
	std::string rep(const QueryResult&qr) const override
	{
		std::ostringstream os;
		os<<qr.sought<<" -occurs "<<qr.lines->size()<<" times."<< std::endl;
		for(auto num: *qr.lines)
			os<<"\t(line"<<num+1<<")"<<*(qr.file->begin()+num)<< std::endl;
		return os.str();
	};
	std::string str1,str2;
	std::string getstr() override{return str1;};
};

main.cpp测试

#include "Query.h"
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
ostream &print(ostream&os,const QueryResult &qr)
{
	os<<qr.sought<<" occurs "<<qr.lines->size()<<" times."<<endl;
	for(auto num: *qr.lines)
		os<<"\t(line"<<num+1<<")"<<*(qr.file->begin()+num)<<endl;
	return os;
}
int main()
{
	ifstream is("importThis.txt");
	if(is.is_open())
	{
		TextQuery t(is);
		Query q1=Query("than");
		Query q2=Query("never");
		Query q3=~q1;
		Query q4=q1|q2;
		Query q5=q1&q2;
		cout<<q1.rep(q1.eval(t))<<endl;
		cout<<q2.rep(q2.eval(t))<<endl;
		cout<<q3.rep(q3.eval(t))<<endl;
		cout<<q4.rep(q4.eval(t))<<endl;
		cout<<q5.rep(q5.eval(t))<<endl;
	}
}

输出

than -occurs 8 times.
        (line3)Beautiful is better than ugly.
        (line4)Explicit is better than implicit.
        (line5)Simple is better than complex.
        (line6)Complex is better than complicated.
        (line7)Flat is better than nested.
        (line8)Sparse is better than dense.
        (line17)Now is better than never.
        (line18)Although never is often better than *right* now.

never -occurs 3 times.
        (line12)Errors should never pass silently.
        (line17)Now is better than never.
        (line18)Although never is often better than *right* now.

than -not occurs 13 times.
        (line1)The Zen of Python, by Tim Peters
        (line2)
        (line9)Readability counts.
        (line10)Special cases aren't special enough to break the rules.
        (line11)Although practicality beats purity.
        (line12)Errors should never pass silently.
        (line13)Unless explicitly silenced.
        (line14)In the face of ambiguity, refuse the temptation to guess.
        (line15)There should be one-- and preferably only one --obvious way to do it.
        (line16)Although that way may not be obvious at first unless you're Dutch.
        (line19)If the implementation is hard to explain, it's a bad idea.
        (line20)If the implementation is easy to explain, it may be a good idea.
        (line21)Namespaces are one honking great idea -- let's do more of those!

than or never -occurs 9 times.
        (line3)Beautiful is better than ugly.
        (line4)Explicit is better than implicit.
        (line5)Simple is better than complex.
        (line6)Complex is better than complicated.
        (line7)Flat is better than nested.
        (line8)Sparse is better than dense.
        (line12)Errors should never pass silently.
        (line17)Now is better than never.
        (line18)Although never is often better than *right* now.

than and never -occurs 2 times.
        (line17)Now is better than never.
        (line18)Although never is often better than *right* now.


C:\Users\xhh\Source\Repos\cppPrimer_15_Query\Debug\cppPrimer_15_Query.exe (进程 132100)已退出,代码为 0。
按任意键关闭此窗口. . .
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值