C++ Prime 第十五章 面向对象程序设计
2023-05-19~2023-05-24
OOP概述
面向对象程序设计的核心思想是数据抽象、继承、动态绑定
继承
通过继承联系在一起的类形成一种层次关系,层次关系的根部是基类、其他类通过继承基类而来,称为派生类。
基类中定义所有派生类的共有成员。
每个派生类各自定义自己特有的成员
虚函数
对于某些函数,基类希望它们各自定义属于各自派生类的版本,就将该函数声明为虚函数
动态绑定
根据传入的对象,来区别调用某个方法。由于该方法是在程序运行时才确定的,所以又称为运行时绑定
15.2.1节练习
练习15.1:
虚成员是值在基类中被声明为虚函数的成员,被声明为虚成员后可以实现动态绑定的特点
练习15.2:
protected允许派生类访问、private不允许派生类访问
练习15.3:
#include<iostream>
using namespace std;
class Quote
{
public:
friend double print_total(ostream& os, const Quote& item, size_t n);
Quote() = default;
Quote(string& n, double p) : id(n), price(p) {};
const string& isbn() const { return id; };
virtual double net_price(size_t n) const { return n * price; };
virtual ~Quote() = default;
protected:
double price = 0.0;
private:
string id;
};
double print_total(ostream& os, const Quote& item, size_t n)
{
double ret = item.net_price(n);
os << "id: " << item.isbn() << "价格: " << ret << endl;
return ret;
}
int main()
{
system("pause");
return 0;
}
15.2.2节练习
练习15.4:
(a)不能自己继承自己
(c)类的声明不需要带上派生类列表
练习15.5:
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& n, double p, size_t t, double disc)
: Quote(n, p), min_qty(t), discount(disc) {};
double net_price(size_t) const override;
private:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const
{
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
练习15.6:
class Quote
{
public:
friend double print_total(ostream& os, const Quote& item, size_t n);
Quote() = default;
Quote(const string& n, double p) : id(n), price(p) {};
const string& isbn() const { return id; };
virtual double net_price(size_t n) const { return n * price; };
virtual ~Quote() = default;
protected:
double price = 0.0;
private:
string id;
};
double print_total(ostream& os, const Quote& item, size_t n)
{
double ret = item.net_price(n);
os << "id: " << item.isbn() << "价格: " << ret << endl;
return ret;
}
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& n, double p, size_t t, double disc)
: Quote(n, p), min_qty(t), discount(disc) {};
double net_price(size_t) const override;
private:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const
{
if (cnt >= min_qty)
return cnt * discount * price;
else
return cnt * price;
}
int main()
{
Quote q("天龙八部1", 999);
print_total(cout, q, 1000);
Bulk_quote b("天龙八部2", 999, 100, 0.8);
print_total(cout, b, 1000);
system("pause");
return 0;
}
练习15.7:
class Limit_Bulk_quote : public Bulk_quote
{
public:
Limit_Bulk_quote() = default;
Limit_Bulk_quote(const string& n, double p, size_t t, double disc, size_t m)
: Bulk_quote(n, p, t, disc), max_qty(m) {};
double net_price(size_t) const override;
private:
size_t max_qty = 0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
if (cnt > min_qty)
{
if (cnt > max_qty)
{
return (max_qty * discount + (max_qty - min_qty)) * price;
}
return min_qty * discount * price;
}
else
return cnt * price;
};
15.2.3节练习
练习15.8:
静态类型:变量声明时的类型或者是表达式生成的类型(编译阶段已知的),
动态类型:程序编译运行后,在内存中保存的变量或者表达式的类型
练习15.9:
用基类的指针或者引用指向派生类
Limit_Bulk_quote l("天龙八部3", 100, 100, 0.8, 150);
Quote& r = l;
Quote* p = &l;
print_total(cout, l, 1000);
练习15.10:
read函数的形参是istream&,实参是一个ifstream类的实例化对象,ifstream的基类是istream,用基类的引用或者指针来接受一个派生类的实例化对象来实现动态绑定
final和override说明符
override:如果在派生类中重写了虚函数加上了override说明符时,如果重写的虚函数形参或者返回类型与基类不同会显示的告诉我们出错,可以及时发现问题
final:被final修饰的函数无法被重写
回避虚函数机制
使用作用域调用相关虚函数即可
cout << p->Quote::net_price(200) << endl; // 调用基类基函数
cout << p->net_price(200) << endl; // 根据动态绑定调用相关版本的虚函数
15.3节练习
练习15.11:
#include<iostream>
using namespace std;
class Quote
{
public:
friend double print_total(ostream& os, const Quote& item, size_t n);
Quote() = default;
Quote(const string& n, double p) : id(n), price(p) {};
const string& isbn() const { return id; };
virtual double net_price(size_t n) const { return n * price; };
virtual void debug();
virtual ~Quote() = default;
protected:
double price = 0.0;
private:
string id;
};
double print_total(ostream& os, const Quote& item, size_t n)
{
double ret = item.net_price(n);
os << "id: " << item.isbn() << "价格: " << ret << endl;
return ret;
}
void Quote::debug()
{
cout << "id、price" << endl;
}
class Bulk_quote :public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& n, double p, size_t t, double disc)
: Quote(n, p), min_qty(t), discount(disc) {};
double net_price(size_t) const override;
void debug() override;
protected:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const
{
if (cnt >= min_qty)
return cnt * discount * price;
else
return cnt * price;
}
void Bulk_quote::debug()
{
cout << "min_qty、discount" << endl;
};
class Limit_Bulk_quote : public Bulk_quote
{
public:
Limit_Bulk_quote() = default;
Limit_Bulk_quote(const string& n, double p, size_t t, double disc, size_t m)
: Bulk_quote(n, p, t, disc), max_qty(m) {};
double net_price(size_t) const override;
void debug() override;
private:
size_t max_qty = 0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
if (cnt > min_qty)
{
if (cnt > max_qty)
{
return (max_qty * discount + (max_qty - min_qty)) * price;
}
return min_qty * discount * price;
}
else
return cnt * price;
};
void Limit_Bulk_quote::debug()
{
cout << "max_qty" << endl;
};
int main()
{
Quote q("天龙八部1", 999);
print_total(cout, q, 1000);
Bulk_quote b("天龙八部2", 100, 100, 0.8);
print_total(cout, b, 200);
Limit_Bulk_quote l("天龙八部3", 100, 100, 0.8, 150);
Quote& r = l;
Quote* p = &l;
print_total(cout, l, 1000);
cout << p->Quote::net_price(200) << endl; // 调用基类基函数
cout << p->net_price(200) << endl; // 根据动态绑定调用相关版本的虚函数
q.debug();
b.debug();
l.debug();
system("pause");
return 0;
}
练习15.12:
有必要,当你希望在当前类需要重写该函数但是在这之后的派生类中不要重写该函数时就要加上overrie、final
练习15.13:
存在问题,上诉代码的功能是派生类希望在重写的虚函数中调用基类的虚函数。但是没有加上基类作用域所以会导致无线递归。加上作用域即可
练习15.14:
调用基类的函数或者属性:(a)、(c)、(e) 调用派生类类的函数或者属性:(b)、(d)、(f)
抽象基类
拥有纯虚函数的类是抽象基类,抽象基类负责定义接口,后续的吉他派生类可以覆盖接口。抽象基类无法实例化
15.4节练习
练习15.16:
#include<iostream>
using namespace std;
class Quote
{
public:
friend double print_total(ostream& os, const Quote& item, size_t n);
Quote() = default;
Quote(const string& n, double p) : id(n), price(p) {};
const string& isbn() const { return id; };
virtual double net_price(size_t n) const { return n * price; };
virtual void debug();
virtual ~Quote() = default;
protected:
double price = 0.0;
private:
string id;
};
double print_total(ostream& os, const Quote& item, size_t n)
{
double ret = item.net_price(n);
os << "id: " << item.isbn() << "价格: " << ret << endl;
return ret;
}
void Quote::debug()
{
cout << "id、price" << endl;
}
class Disc_quote : public Quote
{
public:
Disc_quote() = default;
Disc_quote(const string& n, double p, size_t t, double disc)
: Quote(n, p), min_qty(t), discount(disc) {};
double net_price(size_t) const = 0;
protected:
size_t min_qty = 0;
double discount = 0.0;
};
class Bulk_quote :public Disc_quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& n, double p, size_t t, double disc)
: Disc_quote(n, p, t, disc) {};
double net_price(size_t) const override;
void debug() override;
};
double Bulk_quote::net_price(size_t cnt) const
{
if (cnt >= min_qty)
return cnt * discount * price;
else
return cnt * price;
}
void Bulk_quote::debug()
{
cout << "min_qty、discount" << endl;
};
class Limit_Bulk_quote : public Bulk_quote
{
public:
Limit_Bulk_quote() = default;
Limit_Bulk_quote(const string& n, double p, size_t t, double disc, size_t m)
: Bulk_quote(n, p, t, disc), max_qty(m) {};
double net_price(size_t) const override;
void debug() override;
private:
size_t max_qty = 0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
if (cnt > min_qty)
{
if (cnt > max_qty)
{
return (max_qty * discount + (max_qty - min_qty)) * price;
}
return min_qty * discount * price;
}
else
return cnt * price;
};
void Limit_Bulk_quote::debug()
{
cout << "max_qty" << endl;
};
int main()
{
Quote q("天龙八部1", 999);
print_total(cout, q, 1000);
Bulk_quote b("天龙八部2", 100, 100, 0.8);
print_total(cout, b, 200);
Limit_Bulk_quote l("天龙八部3", 100, 100, 0.8, 150);
Quote& r = l;
Quote* p = &l;
print_total(cout, l, 1000);
cout << p->Quote::net_price(200) << endl; // 调用基类基函数
cout << p->net_price(200) << endl; // 根据动态绑定调用相关版本的虚函数
q.debug();
b.debug();
l.debug();
system("pause");
return 0;
}
练习15.16:
class Limit_Bulk_quote : public Disc_quote
{
public:
Limit_Bulk_quote() = default;
Limit_Bulk_quote(const string& n, double p, size_t t, double disc, size_t m)
: Disc_quote(n, p, t, disc), max_qty(m) {};
double net_price(size_t) const override;
void debug() override;
private:
size_t max_qty = 0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
if (cnt > min_qty)
{
if (cnt > max_qty)
{
return (max_qty * discount + (max_qty - min_qty)) * price;
}
return min_qty * discount * price;
}
else
return cnt * price;
};
练习15.17:
不允许使用抽象类类型的对象
15.5节练习
练习15.18:
合法:Base * p = &d1; p = &dd1;其余均不合法,以非共有方式继承的派生类不能用基类指针或者引用指向它们
练习15.19:
对于Derived_from_Private来说不合法,以非共有方式继承的类的派生类的成员函数和其友元无法用其基类指针指向它
练习15.20:
class A
{
};
class B :protected A
{
};
class C : public B
{
public:
void fun(A& a) {};
};
int main()
{
A* test_a;
B test_b;
test_a = &test_b;
system("pause");
return 0;
}
练习15.21~15.22:
略
15.6节练习
练习15.23:
语句解析:用基类指针指向派生类对象并调用虚函数即为调用派生类重定义的虚函数
用基类指针指向派生类对象并调用非虚函数即为调用基类中的的函数
用派生类1的指针指向派生类2对象并调用非虚函数即为调用派生类1类中的的函数\
15.7.1节练习
练习15.24:
派生类需要动态分配内存的基类,派生类存在动态分配内存的情况下,对应类的虚析构函数需要手动回收内存
15.7.2节练习
练习15.25:
对于该例中删除该构造函数没有什么影响,因为该继承链中的所有类都没有使用默认构造函数进行数据的初始化
15.7.3节练习
练习15.26:
调用顺序如下:
Bulk_quote相关构造函数调用Disc_quote的构造函数,Disc_quote构造函数调用Quote的构造函数。
构造顺序如下:
Quote的构造函数初始化构造完成返回到Disc_quote的构造函数代码区域Disc_quote开始初始化构造,Disc_quote的构造函数初始化完成返回到Bulk_quote的带去区域,Bulk_quote开始初始化构造,Bulk_quote初始化构造完成返回该实例化对象
析构顺序如下:
Bulk_quote->Disc_quote->Quote
#include<iostream>
#include<utility>
using namespace std;
class Quote
{
public:
friend double print_total(ostream& os, const Quote& item, size_t n);
Quote()
{
cout << "Quote默认构造" << endl;
price = 0.0;
id = "";
}
Quote(const Quote& q)
{
cout << "Quote拷贝构造" << endl;
price = q.price;
id = q.id;
}
Quote(Quote&& q) noexcept
{
cout << "Quote移动构造" << endl;
price = std::move(q.price);
id = std::move(q.id);
}
Quote(const string& n, double p) : id(n), price(p) {};
const string& isbn() const { return id; };
virtual double net_price(size_t n) const { return n * price; };
virtual void debug();
virtual ~Quote()
{
cout << "~Quote析构构造" << endl;
}
virtual Quote& operator=(const Quote& q)
{
if (this != &q)
{
cout << "Quote赋值运算符" << endl;
this->~Quote();
id = q.id;
price = q.price;
return *this;
}
return *this;
}
virtual Quote& operator=(Quote&& q) noexcept
{
if (this != &q)
{
cout << "Quote移动赋值运算符" << endl;
this->~Quote();
id = std::move(q.id);
price = std::move(q.price);
}
return *this;
}
protected:
double price = 0.0;
private:
string id;
};
double print_total(ostream& os, const Quote& item, size_t n)
{
double ret = item.net_price(n);
os << "id: " << item.isbn() << "价格: " << ret << endl;
return ret;
}
void Quote::debug()
{
cout << "id、price" << endl;
}
class Disc_quote : public Quote
{
public:
Disc_quote() : Quote()
{
cout << "Disc_quote默认构造" << endl;
min_qty = 0;
discount = 0.0;
}
Disc_quote(const Disc_quote& q) : Quote(q)
{
cout << "Disc_quote拷贝构造" << endl;
min_qty = q.min_qty;
discount = q.discount;
}
Disc_quote(Disc_quote&& q) noexcept : Quote(std::move(q))
{
cout << "Disc_quote移动构造" << endl;
min_qty = std::move(q.min_qty);
discount = std::move(q.discount);
}
Disc_quote& operator=(const Disc_quote& q)
{
Quote::operator=(q);
if (this != &q)
{
cout << "Disc_quote赋值运算符" << endl;
this->~Disc_quote();
min_qty = q.min_qty;
discount = q.discount;
return *this;
}
return *this;
}
Disc_quote& operator=(Disc_quote&& q) noexcept
{
Quote::operator=(std::move(q));
if (this != &q)
{
cout << "Disc_quote移动赋值运算符" << endl;
this->~Disc_quote();
min_qty = std::move(q.min_qty);
discount = std::move(q.discount);
return *this;
}
return *this;
}
Disc_quote(const string& n, double p, size_t t, double disc)
: Quote(n, p), min_qty(t), discount(disc) {};
double net_price(size_t) const = 0;
~Disc_quote() override { cout << "~Disc_quote析构" << endl; };
protected:
size_t min_qty = 0;
double discount = 0.0;
};
class Bulk_quote :public Disc_quote
{
public:
Bulk_quote() : Disc_quote()
{
cout << "Bulk_quote默认构造" << endl;
}
Bulk_quote(const Bulk_quote& q) : Disc_quote(q)
{
cout << "Bulk_quote拷贝构造" << endl;
}
Bulk_quote(Bulk_quote&& q) noexcept : Disc_quote(std::move(q))
{
cout << "Bulk_quote移动构造" << endl;
}
Bulk_quote& operator=(const Bulk_quote& b)
{
Disc_quote::operator=(b);
if (this != &b)
{
cout << "Bulk_quote赋值运算符" << endl;
return *this;
}
return *this;
}
Bulk_quote& operator=(Bulk_quote&& b) noexcept
{
Disc_quote::operator=(std::move(b));
if (this != &b)
{
cout << "Bulk_quote移动赋值运算符" << endl;
return *this;
}
return *this;
}
Bulk_quote(const string& n, double p, size_t t, double disc)
: Disc_quote(n, p, t, disc) {};
~Bulk_quote() override { cout << "~Bulk_quote析构构造" << endl; };
double net_price(size_t) const override;
void debug() override;
};
double Bulk_quote::net_price(size_t cnt) const
{
if (cnt >= min_qty)
return cnt * discount * price;
else
return cnt * price;
}
void Bulk_quote::debug()
{
cout << "min_qty、discount" << endl;
};
class Limit_Bulk_quote : public Disc_quote
{
public:
Limit_Bulk_quote() : Disc_quote()
{
}
Limit_Bulk_quote(const string& n, double p, size_t t, double disc, size_t m)
: Disc_quote(n, p, t, disc), max_qty(m) {};
double net_price(size_t) const override;
void debug() override;
private:
size_t max_qty = 0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
if (cnt > min_qty)
{
if (cnt > max_qty)
{
return (max_qty * discount + (max_qty - min_qty)) * price;
}
return min_qty * discount * price;
}
else
return cnt * price;
};
void Limit_Bulk_quote::debug()
{
cout << "max_qty" << endl;
};
class A
{
};
class B :protected A
{
};
class C : public B
{
public:
void fun(A& a) {};
};
int main()
{
Bulk_quote a;
Bulk_quote b = a;
Bulk_quote c = std::move(b);
system("pause");
return 0;
}
15.7.4节练习
练习15.27:
// Disc_quote:有两个默认实参
Disc_quote(const string& n, double p, size_t t = 0, double disc = 0)
: Quote(n, p), min_qty(t), discount(disc) {};
// Bulk_quote继承Disc_quote的构造函数
//Bulk_quote(const string& n, double p, size_t t, double disc)
// : Disc_quote(n, p, t, disc) {};
using Disc_quote::Disc_quote;
// 的确获得多个构造函数,每个构造函数省略掉一个默认实参
Bulk_quote a("天龙八部1", 999);
Bulk_quote b("天龙八部2", 999, 10);
Bulk_quote c("天龙八部3", 999, 10, 10);
15.8节练习
练习15.28:
vector<shared_ptr<Quote>> vec;
vec.push_back(make_shared<Bulk_quote>("天龙八部1", 999, 10, 0.8));
vec.push_back(make_shared<Bulk_quote>("天龙八部2", 999, 10, 0.8));
vec.push_back(make_shared<Bulk_quote>("天龙八部3", 999, 10, 0.8));
double total = 0;
for (auto temp : vec)
{
total += temp->net_price(10);
}
cout << total << endl;
练习15.29:
如果一样是因为没到最低的打折数量,所以不论是调用基类的net_price还是派生类的net_price价格的计算都是一样的。
15.8.1节练习
练习15.30:
#include<iostream>
#include<utility>
#include<memory>
#include<vector>
#include<set>
using namespace std;
class Quote
{
public:
friend double print_total(ostream& os, const Quote& item, size_t n);
Quote()
{
cout << "Quote默认构造" << endl;
price = 0.0;
id = "";
}
Quote(const Quote& q)
{
cout << "Quote拷贝构造" << endl;
price = q.price;
id = q.id;
}
Quote(Quote&& q) noexcept
{
cout << "Quote移动构造" << endl;
price = std::move(q.price);
id = std::move(q.id);
}
Quote(const string& n, double p) : id(n), price(p) {};
const string& isbn() const { return id; };
virtual double net_price(size_t n) const { return n * price; };
virtual void debug();
virtual ~Quote()
{
cout << "~Quote析构构造" << endl;
}
virtual Quote& operator=(const Quote& q)
{
if (this != &q)
{
cout << "Quote赋值运算符" << endl;
this->~Quote();
id = q.id;
price = q.price;
return *this;
}
return *this;
}
virtual Quote& operator=(Quote&& q) noexcept
{
if (this != &q)
{
cout << "Quote移动赋值运算符" << endl;
this->~Quote();
id = std::move(q.id);
price = std::move(q.price);
}
return *this;
}
virtual Quote* clone() const &
{
return new Quote(*this);
}
virtual Quote* clone() &&
{
return new Quote(std::move(*this));
}
protected:
double price = 0.0;
private:
string id;
};
double print_total(ostream& os, const Quote& item, size_t n)
{
double ret = item.net_price(n);
os << "id: " << item.isbn() << "价格: " << ret << endl;
return ret;
}
void Quote::debug()
{
cout << "id、price" << endl;
}
class Disc_quote : public Quote
{
public:
Disc_quote() : Quote()
{
cout << "Disc_quote默认构造" << endl;
min_qty = 0;
discount = 0.0;
}
Disc_quote(const Disc_quote& q) : Quote(q)
{
cout << "Disc_quote拷贝构造" << endl;
min_qty = q.min_qty;
discount = q.discount;
}
Disc_quote(Disc_quote&& q) noexcept : Quote(std::move(q))
{
cout << "Disc_quote移动构造" << endl;
min_qty = std::move(q.min_qty);
discount = std::move(q.discount);
}
Disc_quote& operator=(const Disc_quote& q)
{
Quote::operator=(q);
if (this != &q)
{
cout << "Disc_quote赋值运算符" << endl;
this->~Disc_quote();
min_qty = q.min_qty;
discount = q.discount;
return *this;
}
return *this;
}
Disc_quote& operator=(Disc_quote&& q) noexcept
{
Quote::operator=(std::move(q));
if (this != &q)
{
cout << "Disc_quote移动赋值运算符" << endl;
this->~Disc_quote();
min_qty = std::move(q.min_qty);
discount = std::move(q.discount);
return *this;
}
return *this;
}
Disc_quote(const string& n, double p, size_t t = 0, double disc = 0)
: Quote(n, p), min_qty(t), discount(disc) {};
double net_price(size_t) const = 0;
~Disc_quote() override { cout << "~Disc_quote析构" << endl; };
protected:
size_t min_qty = 0;
double discount = 0.0;
};
class Bulk_quote :public Disc_quote
{
public:
Bulk_quote() : Disc_quote()
{
cout << "Bulk_quote默认构造" << endl;
}
Bulk_quote(const Bulk_quote& q) : Disc_quote(q)
{
cout << "Bulk_quote拷贝构造" << endl;
}
Bulk_quote(Bulk_quote&& q) noexcept : Disc_quote(std::move(q))
{
cout << "Bulk_quote移动构造" << endl;
}
Bulk_quote& operator=(const Bulk_quote& b)
{
Disc_quote::operator=(b);
if (this != &b)
{
cout << "Bulk_quote赋值运算符" << endl;
return *this;
}
return *this;
}
Bulk_quote& operator=(Bulk_quote&& b) noexcept
{
Disc_quote::operator=(std::move(b));
if (this != &b)
{
cout << "Bulk_quote移动赋值运算符" << endl;
return *this;
}
return *this;
}
Bulk_quote* clone() const& override
{
return new Bulk_quote(*this);
}
Bulk_quote* clone() && override
{
return new Bulk_quote(std::move(*this));
}
//Bulk_quote(const string& n, double p, size_t t, double disc)
// : Disc_quote(n, p, t, disc) {};
using Disc_quote::Disc_quote;
~Bulk_quote() override { cout << "~Bulk_quote析构构造" << endl; };
double net_price(size_t) const override;
void debug() override;
};
double Bulk_quote::net_price(size_t cnt) const
{
if (cnt >= min_qty)
return cnt * discount * price;
else
return cnt * price;
}
void Bulk_quote::debug()
{
cout << "min_qty、discount" << endl;
};
class Limit_Bulk_quote : public Disc_quote
{
public:
Limit_Bulk_quote() : Disc_quote()
{
}
Limit_Bulk_quote(const string& n, double p, size_t t, double disc, size_t m)
: Disc_quote(n, p, t, disc), max_qty(m) {};
double net_price(size_t) const override;
void debug() override;
private:
size_t max_qty = 0;
};
double Limit_Bulk_quote::net_price(size_t cnt) const
{
if (cnt > min_qty)
{
if (cnt > max_qty)
{
return (max_qty * discount + (max_qty - min_qty)) * price;
}
return min_qty * discount * price;
}
else
return cnt * price;
};
void Limit_Bulk_quote::debug()
{
cout << "max_qty" << endl;
};
class Basket
{
public:
void add_item(const Quote& q)
{
items.insert(shared_ptr<Quote>(q.clone()));
}
void add_item(Quote&& q)
{
items.insert(shared_ptr<Quote>(std::move(q).clone()));
}
static bool compare(const shared_ptr<Quote>& l, const shared_ptr<Quote>& r)
{
return l->isbn() < r->isbn();
}
double total_receipt(ostream& os) const
{
double sum = 0.0;
for (auto iter = items.cbegin(); iter != items.cend(); iter = items.upper_bound(*iter))
{
sum += print_total(os, **iter, items.count(*iter));
}
cout << "sum: " << sum << endl;
return sum;
}
private:
multiset<shared_ptr<Quote>, decltype(compare)*> items{compare};
};
int main()
{
Basket bs;
int i = 20;
while (i > 0)
{
bs.add_item(Bulk_quote("天龙八部1", 999, 10, 0.8));
bs.add_item(Quote("天龙八部2", 999));
--i;
}
bs.total_receipt(cout);
system("pause");
return 0;
}
15.9.1节练习
练习15.31:
看不懂题目意思(网上相关信息少,没找到),请教一下会的大佬
15.9.2节练习
练习15.32:
Query中没有定义相关的拷贝、移动、赋值、销毁操作,如果在具体场景中使用它,编译器会按照默认合成的相关函数进行实现
练习15.33:
Query_base是一个抽象基类无法主动实例化该对象,就无法主动的去拷贝、移动、赋值、销毁它。唯一可能出现这些场景的是Query_base的派生类在拷贝、移动、赋值、销毁时会被调用,但是当前的Query_base并没有实现这些函数,所以就算派生类调用它们时也不会发生什么
练习15.9.3节练习
练习15.34~15.36:
(a)、(b)、(c)
#include<string>
#include<algorithm>
#include<utility>
#include<vector>
#include<set>
#include<map>
#include<initializer_list>
#include<fstream>
#include<iostream>
#include<sstream>
using namespace std;
class StrVec
{
public:
// 默认构造函数
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(const initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
// 添加一个移动构造函数
StrVec(StrVec&& s) noexcept : elements(s.elements), first_free(s.first_free),
cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr;
}
StrVec& operator=(StrVec&& s)
{
if (this != &s)
{
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
return *this;
}
// 拷贝构造函数
StrVec(const StrVec&);
// 拷贝赋值运算符
StrVec& operator=(const StrVec& lsv);
StrVec& operator=(initializer_list<string>& s);
// 析构函数
~StrVec();
// 返回当前内存中存储的元素的数量
size_t size() const { return first_free - elements; };
// 返回当前总分配的内存可容纳元素的数量
size_t capacity() const { return cap - elements; };
// 为StrVec扩大内存容量
void reserve(size_t num);
// 从新分配StrVec的元素大小
void resize(size_t num, const string& s);
// 为StrVec添加元素
void push_back(const string& s);
// 返回首元素的迭代器
string* begin() const { return elements; };
// 返回实际元素尾后元素位置迭代器
string* end() const { return first_free; };
// 测试函数
void test();
private:
// 指向分配的内存中的首元素
string* elements;
// 指向实际元素的尾后元素
string* first_free;
// 指向分配的内存中的尾后元素
string* cap;
// 为StrVec分配使用的内存的静态成员
static allocator<string> alloc;
// 内存耗尽后,为StrVec分配新内存
void reallocate();
// 销毁元素并释放内存
void free();
// 保证StrVec有容纳一个新元素的空间,如果没有就调用reallocate分配更多内存来容纳新元素
void chk_n_alloc() { if (size() == capacity()) reallocate(); };
// 工具函数,拷贝构造函数、赋值运算符、析构函数所使用
pair<string*, string*> chk_n_copy(const string* b, const string* e);
// 工具函数,reallocate函数和reserve函数使用
void reallocate_memory(size_t num);
};
allocator<string> StrVec::alloc;
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
void StrVec::free()
{
if (elements)
{
// 使用for_each
for_each(elements, first_free,
[](const string& s) { alloc.destroy(&s); });
//alloc.deallocate(elements, cap - elements);
//elements = nullptr;
//first_free = nullptr;
//cap = nullptr;
//for (auto p = first_free; p != elements;)
//{
// alloc.destroy(--p);
//}
//alloc.deallocate(elements, cap - elements);
}
}
StrVec::~StrVec() { free(); };
pair<string*, string*> StrVec::chk_n_copy(const string* b, const string* e)
{
// 分配内存空间来保存给定范围内的string数据
auto data = alloc.allocate(e - b);
// 将string数据复制到分配的内存空间,并返回一个pair数据,first指向分配的内存空间首地址、second指向分配的内存空间的尾后地址
return { data, uninitialized_copy(b, e, data) };
}
StrVec::StrVec(const StrVec& sv)
{
auto new_data = chk_n_copy(sv.begin(), sv.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
StrVec& StrVec::operator=(const StrVec& lsv)
{
auto new_data = chk_n_copy(lsv.begin(), lsv.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
StrVec& StrVec::operator=(initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
void StrVec::reallocate_memory(size_t num)
{
// 分配新内存
auto new_data = alloc.allocate(num);
// 指向新内存首元素
auto dest = new_data;
// 指向旧内存首元素
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
{
// 将每一个旧内存中的数据移动到到新内存中
alloc.construct(dest++, std::move(*elem++));
};
// 释放旧内存
free();
// 将指针指向新内存
first_free = dest;
elements = new_data;
cap = elements + num;
}
void StrVec::reallocate()
{
// 内存空间不够时将内存空间扩大成当前容量两倍大小
auto newcapacity = size() ? size() * 2 : 1;
reallocate_memory(newcapacity);
}
void StrVec::reserve(size_t num)
{
if (num <= capacity())
{
return;
}
reallocate_memory(num);
}
void StrVec::resize(size_t num, const string& s = "")
{
string* end_iter = nullptr;
if (num < size())
{
end_iter = first_free - (size() - num);
for (auto p = first_free; p != end_iter;)
{
alloc.destroy(--p);
}
}
else if (num > size() && num <= capacity())
{
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
}
else if (num > size() && num > capacity())
{
reallocate_memory(num);
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
}
first_free = end_iter ? end_iter : first_free;
}
void StrVec::test()
{
free();
}
class QueryResult;
class TextQuery
{
public:
using line_no_type = vector<string>::size_type;
TextQuery(ifstream& infile)
{
// 创建vector<string>容器并使用智能指针管理
lines_vec = make_shared<StrVec>();
string line;
string word;
int line_no = 0;
while (getline(infile, line))
{
// 分行保存到vector中
(*lines_vec).push_back(line);
istringstream ss(line);
while (ss >> word)
{
// 在单词与行号(set)的map映射中查找单词是否存在
// 如果存在,在该行号(set)中插入当前行
// 如果不存在,将这个行号(set)指针指向新创建的set
auto& line_no_set = this->word_lineno_map[word];
if (!line_no_set)
{
line_no_set.reset(new set<line_no_type>);
}
// set容器自带去重、排序
line_no_set->insert(line_no);
}
++line_no;
}
}
void test()
{
for (auto temp : *lines_vec)
{
cout << temp << endl;
}
}
QueryResult query(const string& s) const;
private:
shared_ptr<StrVec> lines_vec;
map<string, shared_ptr<set<line_no_type>>> word_lineno_map;
};
class QueryResult
{
using line_no_type = TextQuery::line_no_type;
friend ostream& print(ostream& out, const QueryResult& qr);
public:
QueryResult() = default;
QueryResult(const string& s, shared_ptr<StrVec> l, shared_ptr<set<line_no_type>> l_no) :
word(s), lines(l), lines_no(l_no) {};
private:
shared_ptr<StrVec> lines;
shared_ptr<set<line_no_type>> lines_no;
const string word;
};
QueryResult TextQuery::query(const string& s) const
{
auto ret = this->word_lineno_map.find(s);
if (ret != this->word_lineno_map.end())
{
return QueryResult(s, lines_vec, ret->second);
};
return QueryResult();
};
ostream& print(ostream& out, const QueryResult& qr)
{
if (qr.word == "")
{
out << "not found word" << endl;
return out;
}
out << qr.word << " occurs " << qr.lines_no->size() << " times " << endl;
for (auto line_no : *qr.lines_no)
{
cout << "\t" << "(line " << line_no << ")" << *((*qr.lines).begin() + line_no) << endl;
}
return out;
};
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
string word;
//tq.test();
while (cin >> word)
{
print(cout, tq.query(word));
}
}
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no_type;
virtual ~Query_base() = default;
private:
virtual QueryResult eval(const TextQuery&) const = 0;
virtual string rep() const = 0;
};
class Query
{
private:
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
shared_ptr<Query_base> q;
Query(shared_ptr<Query_base> query) : q(query)
{
cout << "Query(shared_ptr<Query_base> query)构造函数" << endl;
}
public:
Query(const string&);
QueryResult eval(const TextQuery& t) const
{
cout << "Query eval" << endl;
return q->eval(t);
}
string rep() const
{
cout << "Query:rep" << endl;
return q->rep();
}
};
ostream& operator<<(ostream& os, const Query& query)
{
return os << query.rep();
}
class WordQuery : public Query_base
{
friend class Query;
WordQuery(const string& s) : query_word(s)
{
cout << "WordQuery构造函数" << endl;
}
QueryResult eval(const TextQuery& t) const
{
cout << "WordQuery eval" << endl;
return t.query(query_word);
}
string rep() const
{
cout << "WordQuery:rep" << endl;
return query_word;
}
string query_word;
};
class NotQuery : public Query_base
{
friend Query operator~(const Query&);
NotQuery(const Query& q) : query(q) { cout << "NotQuery构造函数" << endl; };
string rep() const
{
cout << "NotQuery:rep" << endl;
return "~(" + query.rep() + ")";
};
QueryResult eval(const TextQuery&) const
{
cout << "NotQuery eval" << endl;
return QueryResult();
};
Query query;
};
class BinaryQuery : public Query_base
{
protected:
BinaryQuery(const Query& l, const Query& r, string s) :lhs(l), rhs(r), opSym(s)
{
cout << "BinaryQuery构造函数" << endl;
}
string rep() const
{
cout << "BinaryQuery:rep" << endl;
return "(" + lhs.rep() + " " + opSym + " " + rhs.rep() + ")";
}
Query lhs, rhs;
string opSym;
};
class AndQuery : public BinaryQuery
{
friend Query operator& (const Query&, const Query&);
AndQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "&")
{
cout << "AndQuery构造函数" << endl;
}
QueryResult eval(const TextQuery&) const
{
cout << "AndQuery eval" << endl;
return QueryResult();
};
};
class OrQuery : public BinaryQuery
{
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "|")
{
cout << "OrQuery构造函数" << endl;
}
QueryResult eval(const TextQuery&) const
{
cout << "QueryResult eval" << endl;
return QueryResult();
};
};
inline Query operator|(const Query& lhs, const Query& rhs)
{
return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
inline
Query operator&(const Query& lhs, const Query& rhs)
{
return shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
inline
Query operator~(const Query& operand)
{
return shared_ptr<Query_base>(new NotQuery(operand));
}
inline
Query::Query(const string& s) : q(new WordQuery(s))
{
cout << "Query::Query(const string& s)构造函数" << endl;
}
int main()
{
ifstream infile("test_word.txt");
//runQueries(infile);
cout << "处理表达式的构造函数:" << endl;
Query q = Query("fiery") & Query("bird") | Query("wind");
cout << "cout<<q:" << endl;
cout << q << endl;
cout << "q.eval():" << endl;
q.eval(infile);
system("pause");
return 0;
}
练习15.37:
略
练习15.38:
BinaryQuery a = Query("fiery") & Query("bir"),不合法BinaryQuery是一个抽象基类无法实例化
AndQuery b = Query("fiery") & Query("bird")、OrQuery c = Query("fiery") & Query("bird")都不合法,重载后的&、|运算符返回的是一个Query类型的对象,无法用一个AndQuery或者OrQuery类型的对象接收
15.9.4节练习
练习15.39~15.40:
#include<string>
#include<algorithm>
#include<utility>
#include<vector>
#include<set>
#include<map>
#include<initializer_list>
#include<fstream>
#include<iostream>
#include<sstream>
using namespace std;
class StrVec
{
public:
// 默认构造函数
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(const initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
// 添加一个移动构造函数
StrVec(StrVec&& s) noexcept : elements(s.elements), first_free(s.first_free),
cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr;
}
StrVec& operator=(StrVec&& s)
{
if (this != &s)
{
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
return *this;
}
// 拷贝构造函数
StrVec(const StrVec&);
// 拷贝赋值运算符
StrVec& operator=(const StrVec& lsv);
StrVec& operator=(initializer_list<string>& s);
// 析构函数
~StrVec();
// 返回当前内存中存储的元素的数量
size_t size() const { return first_free - elements; };
// 返回当前总分配的内存可容纳元素的数量
size_t capacity() const { return cap - elements; };
// 为StrVec扩大内存容量
void reserve(size_t num);
// 从新分配StrVec的元素大小
void resize(size_t num, const string& s);
// 为StrVec添加元素
void push_back(const string& s);
// 返回首元素的迭代器
string* begin() const { return elements; };
// 返回实际元素尾后元素位置迭代器
string* end() const { return first_free; };
// 测试函数
void test();
private:
// 指向分配的内存中的首元素
string* elements;
// 指向实际元素的尾后元素
string* first_free;
// 指向分配的内存中的尾后元素
string* cap;
// 为StrVec分配使用的内存的静态成员
static allocator<string> alloc;
// 内存耗尽后,为StrVec分配新内存
void reallocate();
// 销毁元素并释放内存
void free();
// 保证StrVec有容纳一个新元素的空间,如果没有就调用reallocate分配更多内存来容纳新元素
void chk_n_alloc() { if (size() == capacity()) reallocate(); };
// 工具函数,拷贝构造函数、赋值运算符、析构函数所使用
pair<string*, string*> chk_n_copy(const string* b, const string* e);
// 工具函数,reallocate函数和reserve函数使用
void reallocate_memory(size_t num);
};
allocator<string> StrVec::alloc;
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
void StrVec::free()
{
if (elements)
{
// 使用for_each
for_each(elements, first_free,
[](const string& s) { alloc.destroy(&s); });
//alloc.deallocate(elements, cap - elements);
//elements = nullptr;
//first_free = nullptr;
//cap = nullptr;
//for (auto p = first_free; p != elements;)
//{
// alloc.destroy(--p);
//}
//alloc.deallocate(elements, cap - elements);
}
}
StrVec::~StrVec() { free(); };
pair<string*, string*> StrVec::chk_n_copy(const string* b, const string* e)
{
// 分配内存空间来保存给定范围内的string数据
auto data = alloc.allocate(e - b);
// 将string数据复制到分配的内存空间,并返回一个pair数据,first指向分配的内存空间首地址、second指向分配的内存空间的尾后地址
return { data, uninitialized_copy(b, e, data) };
}
StrVec::StrVec(const StrVec& sv)
{
auto new_data = chk_n_copy(sv.begin(), sv.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
StrVec& StrVec::operator=(const StrVec& lsv)
{
auto new_data = chk_n_copy(lsv.begin(), lsv.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
StrVec& StrVec::operator=(initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
void StrVec::reallocate_memory(size_t num)
{
// 分配新内存
auto new_data = alloc.allocate(num);
// 指向新内存首元素
auto dest = new_data;
// 指向旧内存首元素
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
{
// 将每一个旧内存中的数据移动到到新内存中
alloc.construct(dest++, std::move(*elem++));
};
// 释放旧内存
free();
// 将指针指向新内存
first_free = dest;
elements = new_data;
cap = elements + num;
}
void StrVec::reallocate()
{
// 内存空间不够时将内存空间扩大成当前容量两倍大小
auto newcapacity = size() ? size() * 2 : 1;
reallocate_memory(newcapacity);
}
void StrVec::reserve(size_t num)
{
if (num <= capacity())
{
return;
}
reallocate_memory(num);
}
void StrVec::resize(size_t num, const string& s = "")
{
string* end_iter = nullptr;
if (num < size())
{
end_iter = first_free - (size() - num);
for (auto p = first_free; p != end_iter;)
{
alloc.destroy(--p);
}
}
else if (num > size() && num <= capacity())
{
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
}
else if (num > size() && num > capacity())
{
reallocate_memory(num);
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
}
first_free = end_iter ? end_iter : first_free;
}
void StrVec::test()
{
free();
}
class QueryResult;
class TextQuery
{
public:
using line_no_type = vector<string>::size_type;
TextQuery(ifstream& infile)
{
// 创建vector<string>容器并使用智能指针管理
lines_vec = make_shared<StrVec>();
string line;
string word;
int line_no = 0;
while (getline(infile, line))
{
// 分行保存到vector中
(*lines_vec).push_back(line);
istringstream ss(line);
while (ss >> word)
{
// 在单词与行号(set)的map映射中查找单词是否存在
// 如果存在,在该行号(set)中插入当前行
// 如果不存在,将这个行号(set)指针指向新创建的set
auto& line_no_set = this->word_lineno_map[word];
if (!line_no_set)
{
line_no_set.reset(new set<line_no_type>);
}
// set容器自带去重、排序
line_no_set->insert(line_no);
}
++line_no;
}
}
void test()
{
for (auto temp : *lines_vec)
{
cout << temp << endl;
}
}
QueryResult query(const string& s) const;
private:
shared_ptr<StrVec> lines_vec;
map<string, shared_ptr<set<line_no_type>>> word_lineno_map;
};
class QueryResult
{
using line_no_type = TextQuery::line_no_type;
friend ostream& print(ostream& out, const QueryResult& qr);
public:
shared_ptr<StrVec> get_file()
{
return lines;
}
set<line_no_type>::iterator begin()
{
return (*lines_no).begin();
}
set<line_no_type>::iterator end()
{
return (*lines_no).end();
}
QueryResult() = default;
QueryResult(const string& s, shared_ptr<StrVec> l, shared_ptr<set<line_no_type>> l_no) :
word(s), lines(l), lines_no(l_no) {};
private:
shared_ptr<StrVec> lines;
shared_ptr<set<line_no_type>> lines_no;
const string word;
};
QueryResult TextQuery::query(const string& s) const
{
auto ret = this->word_lineno_map.find(s);
if (ret != this->word_lineno_map.end())
{
return QueryResult(s, lines_vec, ret->second);
};
auto empty_set = make_shared<set<line_no_type>>();
return QueryResult("", lines_vec, empty_set);
};
ostream& print(ostream& out, const QueryResult& qr)
{
if (qr.word == "")
{
out << "not found word" << endl;
return out;
}
out << qr.word << " occurs " << qr.lines_no->size() << " times " << endl;
for (auto line_no : *qr.lines_no)
{
cout << "\t" << "(line " << line_no << ")" << *((*qr.lines).begin() + line_no) << endl;
}
return out;
};
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
string word;
//tq.test();
while (cin >> word)
{
print(cout, tq.query(word));
}
}
class Query;
// 查询接口类,核心功能:
// 1、存储Query_base的基类指针
// 2、通过eval方法调用Query_base派生类的eval方法实现字符串的查询
// 具体实现:
// 1、通过Query的有参(string)构造函数来初始化Query_base的派生类指针(word_query)
// 2、通过Query的有参(share_ptr<Query_base>)构造函数来初始化Query_base的派生类指针(~、and_query、or_query)
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no_type;
// 纯虚析构
virtual ~Query_base() = default;
private:
// 纯虚函数eval接受一个输入流文本对象,返回一个查询结果对象
virtual QueryResult eval(const TextQuery&) const = 0;
// 纯虚函数erp返回表达式
virtual string erp() const = 0;
};
class Word_query : public Query_base
{
public:
Word_query(const string& s) : word(s) {};
QueryResult eval(const TextQuery& t) const override
{
return t.query(word);
}
string erp() const override
{
return word;
}
~Word_query() = default;
private:
string word;
};
class Notoperator;
class Query
{
friend class Notoperator;
friend Query operator~(const Query&);
friend Query operator&(const Query&, const Query&);
friend Query operator|(const Query& left, const Query& right);
public:
// 有参string构造
Query(const string& s) : p(new Word_query(s)) {};
string erp() const
{
return p->erp();
}
// 通过eval方法调用Query_base派生类的eval方法实现字符串的查询
QueryResult eavl(const TextQuery& t) const
{
return p->eval(t);
}
private:
shared_ptr<Query_base> p;
// 有参shared_ptr<Word_query>构造,私有只允许重载友元~调用
Query(shared_ptr<Query_base> share_p) : p(share_p) {};
};
class Notoperator : public Query_base
{
public:
Notoperator(const Query& q) : query(q) {};
QueryResult eval(const TextQuery& t) const override
{
// 获得Qr结果,我们需要对其取反
auto ret = query.eavl(t);
// 获取空set
auto ret_lines = make_shared<set<line_no>>();
auto begin = ret.begin();
auto end = ret.end();
auto sz = ret.get_file()->size();
for (size_t n = 0; n != sz; ++n)
{
if (begin == end || *begin != n)
{
(*ret_lines).insert(n);
}
else if (begin != end)
{
++begin;
}
};
return QueryResult(erp(), ret.get_file(), ret_lines);
}
string erp() const override
{
return "~(" + query.erp() + ")";
}
~Notoperator() = default;
private:
Query query;
};
// 通过Query的有参(share_ptr<Query_base>)构造函数来初始化Query_base的派生类指针(~)
Query operator~(const Query& q)
{
return shared_ptr<Query_base>(new Notoperator(q));
}
// 继承Query_base的抽象基类,抽取成员对象作为AND、OR操作的共有部分
class Binaryquery : public Query_base
{
protected:
Binaryquery(const Query& l, const Query& r, string op) : left(l), right(r), opsym(op) {};
Query left;
Query right;
string opsym;
string erp() const override
{
return "(" + left.erp() + " " + opsym + " " + right.erp() + ")";
}
~Binaryquery() = default;
};
class Andoperator : public Binaryquery
{
public:
Andoperator(const Query& l, const Query& r, string ops = "&") : Binaryquery(l, r, ops) {};
~Andoperator() = default;
QueryResult eval(const TextQuery& t) const override
{
// |求交集、通过set_intersection(set1.beg, set1.end, set2.beg. set2.end, inserter_iter)
auto ret_lines = make_shared<set<line_no>>();
auto left_ret = left.eavl(t);
auto right_ret = right.eavl(t);
// 将左边的查询结果初始化到一个空set中
set_intersection(left_ret.begin(), left_ret.end(), right_ret.begin(), right_ret.end(),
inserter(*ret_lines, ret_lines->begin()));
// 返回交集结果
return QueryResult(erp(), left_ret.get_file(), ret_lines);
}
};
Query operator&(const Query& left, const Query& right)
{
return shared_ptr<Query_base>(new Andoperator(left, right));
}
class Oroperator : public Binaryquery
{
public:
Oroperator(const Query& l, const Query& r, string ops = "|") : Binaryquery(l, r, ops) {};
~Oroperator() = default;
QueryResult eval(const TextQuery& t) const override
{
// |求并集、将左右部分的结果全部set到一起即可
auto left_ret = left.eavl(t);
auto right_ret = right.eavl(t);
// 将左边的查询结果初始化到一个空set中
auto ret_lines = make_shared<set<line_no>>(left_ret.begin(), left_ret.end());
// 将右边的查询结果全部插入到set中
(*ret_lines).insert(right_ret.begin(), right_ret.end());
// 返回并集结果
return QueryResult(erp(), left_ret.get_file(), ret_lines);
}
};
Query operator|(const Query& left, const Query& right)
{
return shared_ptr<Query_base>(new Oroperator(left, right));
}
int main()
{
ifstream infile("test_word.txt");
Query q = (Query("apple") | ~Query("apple1")) & Query("2");
print(cout, q.eavl(infile));
Query q1 = (Query("apple123") | ~Query("apple1123")) & Query("2123");
print(cout, q1.eavl(infile));
system("pause");
return 0;
}
练习15.41:
class Query
{
friend class Notoperator;
friend Query operator~(const Query&);
friend Query operator&(const Query&, const Query&);
friend Query operator|(const Query& left, const Query& right);
friend Query get_Query(Query, const string& s);
public:
// 有参string构造
Query(const string& s) : p(new Word_query(s)), use(new size_t(1)) {};
string erp() const
{
return p->erp();
}
// 通过eval方法调用Query_base派生类的eval方法实现字符串的查询
QueryResult eavl(const TextQuery& t) const
{
return p->eval(t);
}
// 拷贝构造
Query(const Query& q) : p(q.p), use(q.use)
{
++* use;
}
// 拷贝赋值
Query& operator=(Query q)
{
++*q.use;
destroy_q();
p = q.p;
use = q.use;
return *this;
}
~Query()
{
void destroy_q();
}
void destroy_q()
{
if (-- * use == 0)
{
delete use;
delete p;
}
}
private:
size_t* use;
//shared_ptr<Query_base> p;
Query_base* p;
// 有参shared_ptr<Word_query>构造,私有只允许重载友元~调用
Query(Query_base* q_p) : p(q_p) {};
};
练习15.42:
(b)
#include<string>
#include<algorithm>
#include<utility>
#include<vector>
#include<set>
#include<map>
#include<initializer_list>
#include<fstream>
#include<iostream>
#include<sstream>
using namespace std;
class StrVec
{
public:
// 默认构造函数
StrVec() : elements(nullptr), first_free(nullptr), cap(nullptr) {};
StrVec(const initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
// 添加一个移动构造函数
StrVec(StrVec&& s) noexcept : elements(s.elements), first_free(s.first_free),
cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr;
}
StrVec& operator=(StrVec&& s)
{
if (this != &s)
{
free();
elements = s.elements;
first_free = s.first_free;
cap = s.cap;
s.elements = s.first_free = s.cap = nullptr;
}
return *this;
}
// 拷贝构造函数
StrVec(const StrVec&);
// 拷贝赋值运算符
StrVec& operator=(const StrVec& lsv);
StrVec& operator=(initializer_list<string>& s);
// 析构函数
~StrVec();
// 返回当前内存中存储的元素的数量
size_t size() const { return first_free - elements; };
// 返回当前总分配的内存可容纳元素的数量
size_t capacity() const { return cap - elements; };
// 为StrVec扩大内存容量
void reserve(size_t num);
// 从新分配StrVec的元素大小
void resize(size_t num, const string& s);
// 为StrVec添加元素
void push_back(const string& s);
// 返回首元素的迭代器
string* begin() const { return elements; };
// 返回实际元素尾后元素位置迭代器
string* end() const { return first_free; };
// 测试函数
void test();
private:
// 指向分配的内存中的首元素
string* elements;
// 指向实际元素的尾后元素
string* first_free;
// 指向分配的内存中的尾后元素
string* cap;
// 为StrVec分配使用的内存的静态成员
static allocator<string> alloc;
// 内存耗尽后,为StrVec分配新内存
void reallocate();
// 销毁元素并释放内存
void free();
// 保证StrVec有容纳一个新元素的空间,如果没有就调用reallocate分配更多内存来容纳新元素
void chk_n_alloc() { if (size() == capacity()) reallocate(); };
// 工具函数,拷贝构造函数、赋值运算符、析构函数所使用
pair<string*, string*> chk_n_copy(const string* b, const string* e);
// 工具函数,reallocate函数和reserve函数使用
void reallocate_memory(size_t num);
};
allocator<string> StrVec::alloc;
void StrVec::push_back(const string& s)
{
chk_n_alloc();
alloc.construct(first_free++, s);
}
void StrVec::free()
{
if (elements)
{
// 使用for_each
for_each(elements, first_free,
[](const string& s) { alloc.destroy(&s); });
//alloc.deallocate(elements, cap - elements);
//elements = nullptr;
//first_free = nullptr;
//cap = nullptr;
//for (auto p = first_free; p != elements;)
//{
// alloc.destroy(--p);
//}
//alloc.deallocate(elements, cap - elements);
}
}
StrVec::~StrVec() { free(); };
pair<string*, string*> StrVec::chk_n_copy(const string* b, const string* e)
{
// 分配内存空间来保存给定范围内的string数据
auto data = alloc.allocate(e - b);
// 将string数据复制到分配的内存空间,并返回一个pair数据,first指向分配的内存空间首地址、second指向分配的内存空间的尾后地址
return { data, uninitialized_copy(b, e, data) };
}
StrVec::StrVec(const StrVec& sv)
{
auto new_data = chk_n_copy(sv.begin(), sv.end());
elements = new_data.first;
first_free = cap = new_data.second;
}
StrVec& StrVec::operator=(const StrVec& lsv)
{
auto new_data = chk_n_copy(lsv.begin(), lsv.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
StrVec& StrVec::operator=(initializer_list<string>& s)
{
auto new_data = chk_n_copy(s.begin(), s.end());
free();
elements = new_data.first;
first_free = cap = new_data.second;
return *this;
}
void StrVec::reallocate_memory(size_t num)
{
// 分配新内存
auto new_data = alloc.allocate(num);
// 指向新内存首元素
auto dest = new_data;
// 指向旧内存首元素
auto elem = elements;
for (size_t i = 0; i != size(); ++i)
{
// 将每一个旧内存中的数据移动到到新内存中
alloc.construct(dest++, std::move(*elem++));
};
// 释放旧内存
free();
// 将指针指向新内存
first_free = dest;
elements = new_data;
cap = elements + num;
}
void StrVec::reallocate()
{
// 内存空间不够时将内存空间扩大成当前容量两倍大小
auto newcapacity = size() ? size() * 2 : 1;
reallocate_memory(newcapacity);
}
void StrVec::reserve(size_t num)
{
if (num <= capacity())
{
return;
}
reallocate_memory(num);
}
void StrVec::resize(size_t num, const string& s = "")
{
string* end_iter = nullptr;
if (num < size())
{
end_iter = first_free - (size() - num);
for (auto p = first_free; p != end_iter;)
{
alloc.destroy(--p);
}
}
else if (num > size() && num <= capacity())
{
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
}
else if (num > size() && num > capacity())
{
reallocate_memory(num);
end_iter = first_free + (num - size());
for (auto p = first_free; p != end_iter;)
{
alloc.construct(p++, s);
}
}
first_free = end_iter ? end_iter : first_free;
}
void StrVec::test()
{
free();
}
class QueryResult;
class TextQuery
{
public:
using line_no_type = vector<string>::size_type;
TextQuery(ifstream& infile)
{
// 创建vector<string>容器并使用智能指针管理
lines_vec = make_shared<StrVec>();
string line;
string word;
int line_no = 0;
while (getline(infile, line))
{
// 分行保存到vector中
(*lines_vec).push_back(line);
istringstream ss(line);
while (ss >> word)
{
// 在单词与行号(set)的map映射中查找单词是否存在
// 如果存在,在该行号(set)中插入当前行
// 如果不存在,将这个行号(set)指针指向新创建的set
auto& line_no_set = this->word_lineno_map[word];
if (!line_no_set)
{
line_no_set.reset(new set<line_no_type>);
}
// set容器自带去重、排序
line_no_set->insert(line_no);
}
++line_no;
}
}
void test()
{
for (auto temp : *lines_vec)
{
cout << temp << endl;
}
}
QueryResult query(const string& s) const;
private:
shared_ptr<StrVec> lines_vec;
map<string, shared_ptr<set<line_no_type>>> word_lineno_map;
};
class QueryResult
{
using line_no_type = TextQuery::line_no_type;
friend ostream& print(ostream& out, const QueryResult& qr);
public:
shared_ptr<StrVec> get_file()
{
return lines;
}
set<line_no_type>::iterator begin()
{
return (*lines_no).begin();
}
set<line_no_type>::iterator end()
{
return (*lines_no).end();
}
QueryResult() = default;
QueryResult(const string& s, shared_ptr<StrVec> l, shared_ptr<set<line_no_type>> l_no) :
word(s), lines(l), lines_no(l_no) {};
private:
shared_ptr<StrVec> lines;
shared_ptr<set<line_no_type>> lines_no;
const string word;
};
QueryResult TextQuery::query(const string& s) const
{
auto ret = this->word_lineno_map.find(s);
if (ret != this->word_lineno_map.end())
{
return QueryResult(s, lines_vec, ret->second);
};
auto empty_set = make_shared<set<line_no_type>>();
return QueryResult("", lines_vec, empty_set);
};
ostream& print(ostream& out, const QueryResult& qr)
{
if (qr.word == "")
{
out << "not found word" << endl;
return out;
}
out << qr.word << " occurs " << qr.lines_no->size() << " times " << endl;
for (auto line_no : *qr.lines_no)
{
cout << "\t" << "(line " << line_no << ")" << *((*qr.lines).begin() + line_no) << endl;
}
return out;
};
void runQueries(ifstream& infile)
{
TextQuery tq(infile);
string word;
//tq.test();
while (cin >> word)
{
print(cout, tq.query(word));
}
}
class Query;
// 查询接口类,核心功能:
// 1、存储Query_base的基类指针
// 2、通过eval方法调用Query_base派生类的eval方法实现字符串的查询
// 具体实现:
// 1、通过Query的有参(string)构造函数来初始化Query_base的派生类指针(word_query)
// 2、通过Query的有参(share_ptr<Query_base>)构造函数来初始化Query_base的派生类指针(~、and_query、or_query)
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no_type;
// 纯虚析构
virtual ~Query_base() = default;
private:
// 纯虚函数eval接受一个输入流文本对象,返回一个查询结果对象
virtual QueryResult eval(const TextQuery&) const = 0;
// 纯虚函数erp返回表达式
virtual string erp() const = 0;
};
class Word_query : public Query_base
{
public:
Word_query(const string& s) : word(s) {};
QueryResult eval(const TextQuery& t) const override
{
return t.query(word);
}
string erp() const override
{
return word;
}
~Word_query() = default;
private:
string word;
};
class Notoperator;
class Query
{
friend class Notoperator;
friend Query operator~(const Query&);
friend Query operator&(const Query&, const Query&);
friend Query operator|(const Query& left, const Query& right);
friend Query get_Query(Query, const string& s);
public:
// 有参string构造
Query(const string& s) : p(new Word_query(s)) {};
string erp() const
{
return p->erp();
}
// 通过eval方法调用Query_base派生类的eval方法实现字符串的查询
QueryResult eavl(const TextQuery& t) const
{
return p->eval(t);
}
private:
shared_ptr<Query_base> p;
// 有参shared_ptr<Word_query>构造,私有只允许重载友元~调用
Query(shared_ptr<Query_base> share_p) : p(share_p) {};
};
class Notoperator : public Query_base
{
public:
Notoperator(const Query& q) : query(q) {};
QueryResult eval(const TextQuery& t) const override
{
// 获得Qr结果,我们需要对其取反
auto ret = query.eavl(t);
// 获取空set
auto ret_lines = make_shared<set<line_no>>();
auto begin = ret.begin();
auto end = ret.end();
auto sz = ret.get_file()->size();
for (size_t n = 0; n != sz; ++n)
{
if (begin == end || *begin != n)
{
(*ret_lines).insert(n);
}
else if (begin != end)
{
++begin;
}
};
return QueryResult(erp(), ret.get_file(), ret_lines);
}
string erp() const override
{
return "~(" + query.erp() + ")";
}
~Notoperator() = default;
private:
Query query;
};
// 通过Query的有参(share_ptr<Query_base>)构造函数来初始化Query_base的派生类指针(~)
Query operator~(const Query& q)
{
return shared_ptr<Query_base>(new Notoperator(q));
}
// 继承Query_base的抽象基类,抽取成员对象作为AND、OR操作的共有部分
class Binaryquery : public Query_base
{
protected:
Binaryquery(const Query& l, const Query& r, string op) : left(l), right(r), opsym(op) {};
Query left;
Query right;
string opsym;
string erp() const override
{
return "(" + left.erp() + " " + opsym + " " + right.erp() + ")";
}
~Binaryquery() = default;
};
class Andoperator : public Binaryquery
{
public:
Andoperator(const Query& l, const Query& r, string ops = "&") : Binaryquery(l, r, ops) {};
~Andoperator() = default;
QueryResult eval(const TextQuery& t) const override
{
// |求交集、通过set_intersection(set1.beg, set1.end, set2.beg. set2.end, inserter_iter)
auto ret_lines = make_shared<set<line_no>>();
auto left_ret = left.eavl(t);
auto right_ret = right.eavl(t);
// 将左边的查询结果初始化到一个空set中
set_intersection(left_ret.begin(), left_ret.end(), right_ret.begin(), right_ret.end(),
inserter(*ret_lines, ret_lines->begin()));
// 返回交集结果
return QueryResult(erp(), left_ret.get_file(), ret_lines);
}
};
Query operator&(const Query& left, const Query& right)
{
return shared_ptr<Query_base>(new Andoperator(left, right));
}
class Oroperator : public Binaryquery
{
public:
Oroperator(const Query& l, const Query& r, string ops = "|") : Binaryquery(l, r, ops) {};
~Oroperator() = default;
QueryResult eval(const TextQuery& t) const override
{
// |求并集、将左右部分的结果全部set到一起即可
auto left_ret = left.eavl(t);
auto right_ret = right.eavl(t);
// 将左边的查询结果初始化到一个空set中
auto ret_lines = make_shared<set<line_no>>(left_ret.begin(), left_ret.end());
// 将右边的查询结果全部插入到set中
(*ret_lines).insert(right_ret.begin(), right_ret.end());
// 返回并集结果
return QueryResult(erp(), left_ret.get_file(), ret_lines);
}
};
Query operator|(const Query& left, const Query& right)
{
return shared_ptr<Query_base>(new Oroperator(left, right));
}
Query get_Query(Query q, const string& s = "")
{
for (auto beg = s.begin(); beg < s.end(); ++beg)
{
if (*beg == '~')
{
auto or_iter = find(beg + 1, s.end(), '|');
auto and_iter = find(beg + 1, s.end(), '&');
// 返回位置靠前的运算符
auto ret = or_iter < and_iter ? or_iter : and_iter;
// ~后面仍然存在运算符,就递归
if (ret != s.end())
{
return get_Query(shared_ptr<Query_base>(new Notoperator(Query(string(beg + 1, ret)))),
string(ret, s.end()));
}
// ~后面没有运算符了,直接返回结果
else
{
return get_Query(shared_ptr<Query_base>(new Notoperator(Query(string(beg + 1, s.end())))));
}
}
else if (*beg == '|' && beg == s.begin())
{
auto or_iter = find(beg + 1, s.end(), '|');
auto and_iter = find(beg + 1, s.end(), '&');
// 返回位置靠前的运算符
auto ret = or_iter < and_iter ? or_iter : and_iter;
if (*(beg + 1) == '~' && ret != s.end())
{
Query q2 = get_Query(Query(string(beg + 1, ret)),
string(ret, s.end()));
return shared_ptr<Query_base>(new Oroperator(q, q2));
}
// ~后面仍然存在运算符,就递归
if (ret != s.end())
{
return get_Query(
shared_ptr<Query_base>(new Oroperator(q, Query(string(s.begin() + 1, ret)))),
string(ret, s.end()));
}
else
{
return get_Query(shared_ptr<Query_base>(new Oroperator(q, Query(string(beg + 1, s.end())))));
}
}
else if (*beg == '&' && beg == s.begin())
{
auto or_iter = find(beg + 1, s.end(), '|');
auto and_iter = find(beg + 1, s.end(), '&');
// 返回位置靠前的运算符
auto ret = or_iter < and_iter ? or_iter : and_iter;
if (*(beg + 1) == '~' && ret != s.end())
{
Query q2 = get_Query(Query(string(beg + 1, ret)),
string(ret, s.end()));
return shared_ptr<Query_base>(new Andoperator(q, q2));
}
// ~后面仍然存在运算符,就递归
if (ret != s.end())
{
return get_Query(
shared_ptr<Query_base>(new Andoperator(q, Query(string(s.begin() + 1, ret)))),
string(ret, s.end()));
}
else
{
return get_Query(shared_ptr<Query_base>(new Andoperator(q, Query(string(beg + 1, s.end())))));
}
}
else
{
auto or_iter = find(beg + 1, s.end(), '|');
auto and_iter = find(beg + 1, s.end(), '&');
// 返回位置靠前的运算符
auto ret = or_iter < and_iter ? or_iter : and_iter;
return get_Query(
shared_ptr<Query_base>(new Word_query(string(s.begin(), ret))),
string(ret, s.end()));
}
}
if (s == "")
{
return q;
}
else
{
Query WordQuery(s);
return shared_ptr<Query_base>(WordQuery.p);
}
}
void func(const TextQuery& infile)
{
vector<string> query_history;
string query_word;
int chioce = 0;
string word = "";
cout << "按1、进入历史查询" << endl;
cout << "按2、开始查询" << endl;
do
{
int chiose_num = 0;
switch (chioce)
{
case 1:
system("cls");
if (query_history.size() == 0)
{
cout << "历史查询为空" << endl;
cout << "按任意键返回主菜单" << endl;
system("pause");
break;
}
else
{
int i;
while (true)
{
system("cls");
i = 1;
for (auto temp : query_history)
{
cout << i << "、:" << temp << endl;
++i;
}
cout << "输入序号返回历史查询语句" << endl;
cin >> chiose_num;
if (chiose_num-1 < query_history.size() && chiose_num > 0)
{
word += query_history[chiose_num - 1];
cout << "语句已保存,可进入查询功能进行查询,当前查询语句为:" << word << endl;
cout << "按0返回主菜单,按其他键继续添加历史语句" << endl;
}
cin >> chiose_num;
if (chiose_num == 0)
{
break;
}
}
}
break;
case 2:
string input_str = "";
system("cls");
cout << "输入要查询的表达式" << endl;
cout << "参考如下格式:apple|~bac&bce&~app" << endl;
if (word != "")
{
cout << "当前已有查询语句为" << word << endl;
cout << "是否保留该语句与后续输入语句合并查询?(输入1保留、输入0不保留)" << endl;
cin >> chiose_num;
if (chiose_num == 0)
{
word = "";
cout << "请输入查询语句" << endl;
}
else
{
cout << "请输入查询语句" << endl;
cout << word;
}
}
if (cin >> input_str)
{
auto ret = get_Query(Query(""), word + input_str);
print(cout, ret.eavl(infile));
query_history.push_back(word + input_str);
}
cout << "查询完成" << endl;
cout << "按任意键返回主菜单" << endl;
system("pause");
break;
};
system("cls");
cout << "按1、进入历史查询" << endl;
cout << "按2、开始查询" << endl;
} while (cin >> chioce);
}
int main()
{
ifstream infile("test_word.txt");
func(infile);
system("pause");
return 0;
}
勇敢和愚蠢只有一剑之差