笔记较为零散,都是自己不熟悉的知识点。
习题答案至于一个.h 和.cc 中,需要演示某一题直接修改 #define NUM****, 如运行15.30题为#define NUM1530;
chapter 15
1. 面向对象程序设计的核心思想是数据抽象、继承和动态绑定。通过使用数据抽象,我们可以将类的实现和接口分离。
使用继承可以定义相似额类型并对其相似关系建模:使用动态绑定,可以在一定程度上忽略相似类型的区别,而以统一的方式使用它们的对象。
基类将类型相关的函数与派生类不做改变直接继承的函数区分对待。对于某些函数,基类希望他的派生类各自定义适合自身的版本,此时基类
就将这些函数声明成虚函数。
函数的运行版本由实参决定,即在运行时选择函数的版本,所以动态绑定又被称为运行时绑定。
2. 基类通常都应该定义一个虚析构函数、即使该函数不执行任何实际操作也是如此。
派生类可以继承其基类的成员,然而当遇到net_price这样与类型相关的操作时,派生类必须对其重新定义。也就是说,派生类需要对这些操作
提供自己的新定义以覆盖override从基类继承而来的旧定义。
c++11标准允许派生类显示地注明它使用某个成员函数覆盖了它继承的虚函数。具体是在形参列表后面/const关键字后面/引用成员函数的引用
限定符后面添加一个关键字override.
每个类控制它自己的成员初始化过程。每个类负责定义各自的接口,想要与类的对象交互必须使用该类的接口,即使这个对象时派生类的基类部分也是如此。
因此,派生类对象不能直接初始化基类的成员。派生类应该遵循基类的接口,并且通过调用基类的构造函数来初始化那些从基类中继承而来的成员。
不论从基类中派生出来多少个派生类,对于每个静态成员来说只存在唯一的实例。
3. 可以将基类的指针或引用绑定到派生类对象上有一层极为重要的含义:当使用基类的引用或指针时,实际上我们并不清楚该引用或指针所绑定对象的
真实类型。该对象可能是基类的对象,也可能是派生类的对象。
动态类型则是变量或表达式表示的内存中的对象的类型。动态类型直到运行时才可知。
因为一个基类的对象可能是派生类对象的一部分,也可能不是,所以不存在从基类向派生类的自动类型转换。
当我们用一个派生类对象为一个基类对象初始化或赋值时,只有该派生类对象中的基类部分会被拷贝、移动或赋值。
重要的一点: 动态绑定只有当我们通过指针或引用调用虚函数时才会发生。
4. 如果我们用override标记了某个函数,但该函数并没有覆盖已存在的虚函数,此时编译器会报错。
我们不能创建抽象基类的对象。
派生类的成员和友元只能访问派生类对象中的基类部分的受保护成员;对于普通的基类对象中的成员不具有特殊的访问权限。
对于代码中的某个给定节点来说,如果基类的公有成员是可访问的,则派生类向基类的类型转换也是可访问的;反之则不行。
不能继承友员关系;每个类负责控制各自成员的访问权限。
为什么基类与派生类中的虚函数必须有相同的形参列表。加入基类与派生类的虚函接受的实参不同,则我们就无法通过基类的引用或指针调用派生类的虚函数。
5. 如果我们删除的是一个指向派生类对象的基类指针,则需要虚析构函数。
如果基类的析构函数不是虚函数,则delete一个指向派生类对象的基类指针将产生未定义的行为。
当派生类定义了拷贝或移动操作时,该操作负责拷贝或移动包括基类部分成员在内的整个对象。
如果构造函数或析构函数调用了某个虚函数,则我们应该执行与构造函数或析构函数所属类型相对应的虚函数版本。
6. 当派生类对象被赋值给基类对象时,其中的派生类部分将被“切掉”,因此容器和存在继承关系的类型无法兼容。
//Chapter15.h
#ifndef CHAPTER15_H
#define CHAPTER15_H
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <memory>
#include <set>
#include <map>
#include <vector>
#include <initializer_list>
#include <algorithm>
#include <stdexcept>
using namespace std;
/*15.3*/
class Quote{
friend bool operator !=(const Quote& lhs, const Quote &rhs);
public:
Quote() {cout << "Default constructing Quote. "<<endl; }
Quote(const string &b, double p): bookNo(b), price(p) {cout <<"Quote:constructor taking 2 parameters." <<endl;}
Quote(const Quote &q): bookNo(q.bookNo), price(q.price){
cout << "Quote:copy costructing\n";
}
Quote(Quote &&q) noexcept : bookNo(move(q.bookNo)), price(move(q.price)){
cout << "Quote:move constructing\n";
}
Quote operator = (const Quote &rhs){
if(*this != rhs){
bookNo = rhs.bookNo;
price = rhs.price;
}
cout << "Quote:copy=() \n";
return *this;
}
Quote& operator =(Quote &rhs)noexcept{
if(*this != rhs){
bookNo = move(rhs.bookNo);
bookNo = move(rhs.price);
}
cout << "Quote:move != \n"<<endl;
return *this;
}
virtual Quote* clone()const& { return new Quote(*this); }
// virtual Quote* clone()&& { return new Quote(move(*this)); }
string isbn()const { return bookNo; }
virtual double net_price(size_t n) const { return n*price; }
virtual void debug() const;
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price;
};
bool inline operator !=(const Quote& lhs, const Quote& rhs){
return lhs.bookNo != rhs.bookNo && lhs.price != rhs.price;
}
void Quote::debug() const{
cout << "Data members of this class: \n" << "bookNo: " << isbn() << " "
<< "price: " << this ->price <<" ";
}
/*15.3*/
double print_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;
}
/*15.4*/
class Bulk_quote : public Quote{
public:
Bulk_quote() { cout <<"default constructing Bulk_quote \n"<<endl;}
Bulk_quote(const string &book, double p, size_t q, double d): Quote(book, p), min_qty(q), discount(d) {
cout << "Bulk_quote: constructor takes 4 parameters.\n";
}
Bulk_quote(const Bulk_quote &rhs): Quote(rhs){
cout << "Bulk_quote: copy constructor.\n";
}
//! move constructor
Bulk_quote(Bulk_quote&& bq): Quote(move(bq)){
cout << "Bulk_quote : move constructor\n";
}
//! copy =()
Bulk_quote& operator =(const Bulk_quote& rhs){
Quote::operator =(rhs);
cout << "Bulk_quote : copy =()\n";
return *this;
}
//! move =()
Bulk_quote& operator =(Bulk_quote&& rhs) noexcept {
Quote::operator =(move(rhs));
cout << "Bulk_quote : move =()\n";
return *this;
}
Bulk_quote* clone()const& { return new Bulk_quote(*this); }
double net_price(size_t n) const override{
return n * price * (n >= min_qty ? 1- discount : 1);
}
void debug() const override;
private:
size_t min_qty;
double discount;
};
void Bulk_quote::debug() const{
Quote::debug();
cout << "min_qty= " <<this->min_qty << " " <<"discount= " << this->discount <<" ";
}
/*15.7*/
class Limit_quote: public Quote{
public:
Limit_quote();
Limit_quote(const string &book, double p, size_t mq, double d): Quote(book, p), max_qty(mq), discount(d) { }
double net_price(size_t n)const override{
if(n <= max_qty)
return n * price * (1 - discount);
else
return (n - max_qty) * price + max_qty * price * (1- discount);
// return n * price * (n <= max_qty ? 1 - discount : 1);
}
void debug() const override;
Limit_quote* clone()const& { return new Limit_quote(*this); }
private:
size_t max_qty;
double discount;
};
void Limit_quote::debug() const{
Quote::debug();
cout << "max_qty= " <<this->max_qty << " " <<"discount= " << this->discount <<" ";
}
/*15.11*/
void print_debug(const Quote &q){
q.debug();
}
/*15.13*/
class base
{
public:
string name() { return basename; }
virtual void print(ostream &os) { os << basename; }
//print用来输出基类的basename.
private:
string basename = "base\n";
};
class derived : public base
{
public:
void print(ostream &os) { base::print(os); os << " derived\n " << i; }
private:
int i;
};
/*15.15*/
class Disc_quote : public Quote{
public:
Disc_quote() = default;
Disc_quote(const string &b, double p, size_t q, double d): Quote(b, p), quantity(q), discount(d){ }
virtual double net_price(size_t n)const override = 0;
protected:
size_t quantity;
double discount;
};
class Bulk_quote1 : public Disc_quote{
public:
Bulk_quote1() = default;
Bulk_quote1(const string &b, double p, size_t q, double dis):Disc_quote(b, p, q, dis){ }
double net_price(size_t n) const override;
void debug() const override;
};
double Bulk_quote1::net_price(size_t n) const{
return n * price * (n >= quantity ? 1-discount : 1);
}
void Bulk_quote1::debug() const{
Quote::debug();
cout << "min_qty= " << quantity <<" " <<"discount= " << discount<< " ";
}
/*15.16*/
class Limit_quote_16: public Disc_quote{
public:
Limit_quote_16() = default;
Limit_quote_16(const string &book, double p, size_t max, double d): Disc_quote(book, p, max, d) { }
double net_price(size_t n)const override{
if(n <= quantity)
return n * price * (1 - discount);
else
return (n - quantity) * price + quantity * price * (1- discount);
}
void debug() const override;
};
void Limit_quote_16::debug() const{
Quote::debug();
cout << "max_qty= " <<this->quantity << " " <<"discount= " << this->discount <<" ";
}
/*15.18*/
class Base
{
public:
void pub_mem(); // public member
protected:
int prot_mem; // protected member
private:
char priv_mem; // private member
};
struct Pub_Derv : public Base{
void memfcn(Base &b) { b = *this; }
};
struct Priv_Derv : private Base{
void memfcn(Base &b) { b = *this; }
};
struct Prot_Derv : protected Base{
void memfcn(Base &b) { b = *this; }
};
struct Derived_from_Public : public Pub_Derv{
void memfcn(Base &b) { b = *this; }
};
struct Derived_from_Private : public Priv_Derv{
//void memfcn(Base &b) { b = *this; }
};
struct Derived_from_Protected : public Prot_Derv{
void memfcn(Base &b) { b = *this; }
};
/*15.21*/
class Shape{
public:
typedef pair<double, double> Coordinate;
Shape() = default;
Shape(const string& n): name(){ }
virtual double area() const = 0;
virtual double perimeter() const = 0;
virtual ~Shape() = default;
private:
string name;
};
class Rectangle : public Shape{
public:
Rectangle() = default;
Rectangle(const string &n, const Coordinate &a, const Coordinate &b,
const Coordinate &c, const Coordinate &d): Shape(n), a(a), b(b), c(c), d(d){}
double area() const;
~Rectangle() = default;
protected:
Coordinate a;
Coordinate b;
Coordinate c;
Coordinate d;
};
class Square : public Rectangle{
public:
Square() = default;
Square(const string &n, const Coordinate &a, const Coordinate &b,
const Coordinate &c, const Coordinate &d): Rectangle(n, a, b, c, d){}
double area() const;
~Square() = default;
};
/*15.23*/
class Base_23{
public:
virtual int fcn(){ cout << "Base::fcn()\n"; return 0; }
};
class D1: public Base_23{
public:
int fcn() override {cout << "D1::fcn()\n"; return 0;}
virtual void f2() {cout << "D1::f2()\n"; }
};
class D2 : public D1{
public:
int fcn(int);
int fcn() override{ cout << "D2::fcn()\n"; return 0;}
void f2() override{ cout << "D2::f2()\n"; }
};
/*15.27*/
class Bulk_quote_27 : public Disc_quote{
public:
Bulk_quote_27() {cout << "default constructing Bulk_quote\n"; }
/*
Bulk_quote(const string& b, double p, size_t q, double disc) :
Disc_quote(b,p,q,disc) { cout << "Bulk_quote : constructor taking 4 parameters\n"; }
*/
using Disc_quote::Disc_quote;
//! copy constructor
Bulk_quote_27(const Bulk_quote_27& bq) : Disc_quote(bq) { cout << "Bulk_quote_27 : copy constructor\n"; }
//! move constructor
Bulk_quote_27(Bulk_quote_27&& bq) : Disc_quote(move(bq)) {
cout << "Bulk_quote_27 : move constructor\n";
}
//! copy =()
Bulk_quote_27& operator =(const Bulk_quote_27& rhs){
Disc_quote::operator =(rhs);
cout << "Bulk_quote_27 : copy =()\n";
return *this;
}
//! move =()
Bulk_quote_27& operator =(Bulk_quote_27&& rhs){
Disc_quote::operator =(move(rhs));
cout << "Bulk_quote : move =()\n";
return *this;
}
Bulk_quote_27* clone()const& { return new Bulk_quote_27(*this); }
// Bulk_quote_27* clone()&& { return new Bulk_quote_27(move(*this)); }
double net_price(size_t n) const override;
void debug() const override;
~Bulk_quote_27() override {
cout << "destructing Bulk_quote\n";
}
};
double Bulk_quote_27::net_price(size_t n) const{
return n * price * ( n >= quantity ? 1 - discount : 1);
}
void Bulk_quote_27::debug() const{
cout //<< "data members of this class:\n"
<< "min_qty= " << quantity << " "
<< "discount= " << this->discount<< " \n";
}
/*15.30*/
class Basket{
public:
//! copy verison
// void add_item(const Quote& sale){
// items.insert(shared_ptr<Quote>(sale.clone())); }
void add_item(const shared_ptr<Quote> &sale){ items.insert(sale); }
//! move version
// void add_item(Quote&& sale){
// items.insert(shared_ptr<Quote>(move(sale).clone())); }
double total_receipt(ostream& os) const;
private:
//! function to compare needed by the multiset member
static bool compare(const shared_ptr<Quote>& lhs,
const shared_ptr<Quote>& rhs){
return lhs->isbn() < rhs->isbn();
}
//! hold multiple quotes, ordered by the compare member
multiset<shared_ptr<Quote>, decltype(compare)*> items{compare};
};
double Basket::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));
} //! ^^^^^^^^^^^^^ using count to fetch
//! the number of the same book.
os << "Total Sale: " << sum << endl;
return sum;
}
//15.35
// forward declaration needed for friend declaration in StrBlob
class StrBlobPtr;
class StrBlob{
friend class StrBlobPtr;
public:
typedef vector<string>::size_type size_type;
// constructors
StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob(initializer_list<string> il);
// size operations
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
// add and remove elements
void push_back(const string &t) { data->push_back(t); }
void pop_back();
// element access
string& front();
string& back();
// interface to StrBlobPtr
StrBlobPtr begin(); // can't be defined until StrBlobPtr is
StrBlobPtr end();
private:
shared_ptr<vector<string>> data;
// throws msg if data[i] isn't valid
void check(size_type i, const string &msg) const;
};
// StrBlobPtr throws an exception on attempts to access a nonexistent element
class StrBlobPtr{
friend bool eq(const StrBlobPtr&, const StrBlobPtr&);
public:
StrBlobPtr(): curr(0) { }
StrBlobPtr(StrBlob &a, size_t sz = 0) : wptr(a.data), curr(sz) { }
//! newly overloaded why?
StrBlobPtr(const StrBlob &a, const size_t sz = 0) : wptr(a.data), curr(sz) { }
string& deref() const;
StrBlobPtr& incr(); // prefix version
StrBlobPtr& decr(); // prefix version
private:
// check returns a shared_ptr to the vector if the check succeeds
shared_ptr<vector<string>>
check(size_t, const string&) const;
// store a weak_ptr, which means the underlying vector might be destroyed
weak_ptr<vector<string>> wptr;
size_t curr; // current position within the array
};
class QueryResult;
/**
* @brief The TextQuery class using StrBlob
*/
class TextQuery{
public:
typedef StrBlob::size_type line_no;
//! constructor
TextQuery(ifstream& fin);
//! query operation
QueryResult query(const string&) const;
private:
//! data members
StrBlob file;
map<string, shared_ptr<set<line_no>>> wordMap;
};
/**
* @brief Query Result
*/
class QueryResult
{
friend std::ostream& operator<<(std::ostream&, const QueryResult&);
friend ostream& print(ostream&, const QueryResult&);
public:
//! constructor
QueryResult(std::string s,
std::shared_ptr<std::set<TextQuery::line_no>> sp_l,
StrBlob f) :
sought(s), sp_lines(sp_l), file(f) { }
//! added for ex12.33
//! ? Think about wether the "const"s here are expected.
const StrBlob& get_file() const{ return file; }
std::set<TextQuery::line_no>::iterator begin() { return sp_lines->begin(); }
std::set<TextQuery::line_no>::iterator end() { return sp_lines->end(); }
private:
//! three data members
std::string sought;
std::shared_ptr<std::set<TextQuery::line_no>> sp_lines;
StrBlob file;
};
ostream& print(ostream&, const QueryResult&);
/**
* @brief print the result to the output stream specified.
* @note class QueryResult's friend
*/
ostream& operator<<(std::ostream &os, const QueryResult &qr)
{
os << qr.sought << " occurs " << qr.sp_lines->size() << " "
<< "times" << "\n";
//! print each line in which the word appears
for ( auto &index : *qr.sp_lines)
{
os << "\t(line " << index + 1 << ") ";
const StrBlobPtr wp(qr.file, index);
os << wp.deref() << "\n";
}
return os;
}
/**
* @brief abstract class acts as a base class for all concrete query types
* all members are private.
*/
class Query_base
{
friend class Query;
protected:
using line_no = TextQuery::line_no; // used in the eval function
virtual ~Query_base() = default;
private:
//! returns QueryResult that matches this query
virtual QueryResult eval(const TextQuery&) const = 0;
//! a string representation of this query
virtual string rep() const = 0;
};
/**
* @brief The WordQuery class
*The only class that actually performs a query on the given TextQuery object.
*No public members defined in this class. All operation are through the friend
*class Query.
*/
class WordQuery : public Query_base{
//! class Query uses the WordQuery constructor
friend class Query;
WordQuery(const string& s): query_word(s) {
cout << "WordQuery::WordQuery(" + s + ")\n";
}
//! virtuals:
QueryResult eval(const TextQuery& t) const override
{ return t.query(query_word); }
string rep() const override {
cout << "WodQuery::rep()\n";
return query_word;
}
string query_word;
};
/**
* @brief interface class to manage the Query_base inheritance hierachy
*/
class Query
{
friend Query operator~(const Query&);
friend Query operator|(const Query&, const Query&);
friend Query operator&(const Query&, const Query&);
public:
//! build a new WordQuery
Query(const string& s) : q(new WordQuery(s)) {
cout << "Query::Query(const string& s) where s="+s+"\n";
}
//! interface functions: call the corresponding Query_base operatopns
QueryResult eval(const TextQuery& t) const { return q->eval(t); }
string rep() const
{
cout << "Query::rep() \n";
return q->rep();
}
private:
//! constructor only for friends
Query(shared_ptr<Query_base> query) : q(query) {
cout << "Query::Query(shared_ptr<Query_base> query)\n";
}
shared_ptr<Query_base> q;
};
inline ostream& operator << (ostream& os, const Query& query){
//! make a virtual call through its Query_base pointer to rep();
return os << query.rep();
}
/**
* @brief The BinaryQuery class
*An abstract class holds data needed by the query types that operate on two operands
*/
class BinaryQuery : public Query_base{
protected:
BinaryQuery(const Query&l, const Query& r, string s):
lhs(l), rhs(r), opSym(s) {
cout << "BinaryQuery::BinaryQuery() where s=" + s + "\n";
}
//! @note: abstract class: BinaryQuery doesn't define eval
string rep() const override {
cout << "BinaryQuery::rep()\n";
return "(" + lhs.rep() + " "
+ opSym + " "
+ rhs.rep() + ")";
}
Query lhs, rhs;
string opSym;
};
class OrQuery :public BinaryQuery{
friend Query operator|(const Query&, const Query&);
OrQuery(const Query& left, const Query& right):
BinaryQuery(left, right, "|") {
cout << "OrQuery::OrQuery\n";
}
QueryResult eval(const TextQuery& )const override;
};
inline Query operator|(const Query &lhs, const Query& rhs){
return shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}
QueryResult OrQuery::eval(const TextQuery &text) const{
QueryResult right = rhs.eval(text), left= lhs.eval(text);
//! copy the left-hand operand into the result set
shared_ptr<set<line_no>> ret_lines =
make_shared<set<line_no>>(left.begin(), left.end());
//! inert lines from the right-hand operand
ret_lines->insert(right.begin(), right.end());
return QueryResult(rep(),ret_lines,left.get_file());
}
/**
* @brief The NotQuery class
*
*The ~ operator generates a NotQuery, which holds a Query,
*which it negates.
*/
class NotQuery : public Query_base{
friend Query operator~(const Query& operand);
NotQuery(const Query& q): query(q) {
cout << "NotQuery::NotQuery()\n";
}
//! virtuals:
string rep() const override {
cout << "NotQuery::rep()\n";
return "~(" + query.rep() + ")";
}
QueryResult eval(const TextQuery &) const override;
Query query;
};
inline Query operator~(const Query& operand){
return shared_ptr<Query_base>(new NotQuery(operand));
//! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! note : There is an imlplicit conversion here.
//! The Query constructor that takes shared_ptr is not
//! "explicit", thus the compiler allows this conversion.
}
/**
* @brief NotQuery::eval
* @return the lines not in its operand's result set
*/
QueryResult NotQuery::eval(const TextQuery &text) const{
//! virtual call to eval through the Query operand
QueryResult result = query.eval(text);
//! start out with an empty result set
shared_ptr<set<line_no>>
ret_lines = make_shared<set<line_no>>();
set<TextQuery::line_no>::iterator
begin = result.begin(),
end = result.end();
StrBlob::size_type sz = result.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(rep(), ret_lines, result.get_file());
}
class AndQuery : public BinaryQuery{
friend Query operator&(const Query&, const Query&);
AndQuery(const Query& left, const Query& right):
BinaryQuery(left,right, "&") {
cout << "AndQuery::AndQuery()\n";
}
//! @note: inherits rep and define eval
QueryResult eval(const TextQuery &) const override;
};
inline Query operator& (const Query& lhs, const Query& rhs){
return shared_ptr<Query_base>(new AndQuery(lhs,rhs));
}
/**
* @brief AndQuery::eval
* @return the intersection of its operands' result sets
*/
QueryResult AndQuery::eval(const TextQuery &text) const{
//! virtual calls through the Query operands to get result sets for the operands
QueryResult left = lhs.eval(text), right = rhs.eval(text);
//! set to hold the intersection of the left and right
shared_ptr<set<line_no>>
ret_lines = make_shared<set<line_no>>();
//! writes the intersection of two ranges to a destination iterator
set_intersection(left.begin(), left.end(),
right.begin(), right.end(),
inserter(*ret_lines, ret_lines->begin()));
return QueryResult(rep(), ret_lines, left.get_file());
}
//15.42
class QueryHistory
{
public:
Query& operator[](size_t n) {
return *(query_vec[n]);
}
//return the assigned number of the new query
size_t add_query(const Query&);
private:
vector<shared_ptr<Query>> query_vec;
};
size_t QueryHistory::add_query(const Query &query){
shared_ptr<Query> p = make_shared<Query>(query);
query_vec.push_back(p);
return query_vec.size() - 1;
}
#endif
//main.cc
#include <fstream>
#include <iostream>
#include <vector>
#include <memory>
#include "Chapter15.h"
using namespace std;
#define NUM1539
/*15.35*/
// constructor
inline
StrBlob::StrBlob(initializer_list<string> il):
data(make_shared<vector<string>>(il)) { }
inline
string& StrBlobPtr::deref() const{
auto p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
inline
shared_ptr<vector<string>>
StrBlobPtr::check(size_t i, const string &msg) const{
auto ret = wptr.lock(); // is the vector still around?
if (!ret)
throw runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret; // otherwise, return a shared_ptr to the vector
}
// prefix: return a reference to the incremented object
inline
StrBlobPtr& StrBlobPtr::incr(){
// if curr already points past the end of the container, can't increment it
check(curr, "increment past end of StrBlobPtr");
++curr; // advance the current state
return *this;
}
inline
StrBlobPtr& StrBlobPtr::decr(){
// if curr is zero, decrementing it will yield an invalid subscript
--curr; // move the current state back one element}
check(-1, "decrement past begin of StrBlobPtr");
return *this;
}
// begin and end members for StrBlob
inline
StrBlobPtr
StrBlob::begin() {
return StrBlobPtr(*this);
}
inline
StrBlobPtr
StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
// named equality operators for StrBlobPtr
inline
bool eq(const StrBlobPtr &lhs, const StrBlobPtr &rhs){
auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
// if the underlying vector is the same
if (l == r)
// then they're equal if they're both null or
// if they point to the same element
return (!r || lhs.curr == rhs.curr);
else
return false; // if they point to difference vectors, they're not equal
}
inline
bool neq(const StrBlobPtr &lhs, const StrBlobPtr &rhs){
return !eq(lhs, rhs);
}
/**
* @brief constructor using StrBlob.
*/
TextQuery::TextQuery(ifstream &fin) : file(StrBlob()), wordMap(map<string,shared_ptr<set<line_no>>>()){
string line;
//! each line
while(getline(fin, line))
{
file.push_back(line);
int n = file.size() - 1; //! the current line number
//! each word
stringstream lineSteam(line);
string word;
while(lineSteam >> word) {
shared_ptr<set<line_no>>&
sp_lines = wordMap[word];
//! if null
if(!sp_lines) {
sp_lines.reset(new set<line_no>);
}
sp_lines->insert(n);
}
}
}
/**
* @brief do a query opertion and return QueryResult object.
*/
QueryResult
TextQuery::query(const string &sought) const{
//! dynamicaly allocated set used for the word does not appear.
static shared_ptr<set<line_no>> noData(new set<line_no>);
//! fetch the iterator to the matching element in the map<word, lines>.
//map<string, shared_ptr<set<index_Tp>>>::const_iterator
auto iter = wordMap.find(sought);
if(iter == wordMap.end())
return QueryResult(sought, noData, file);
else
return QueryResult(sought, iter->second, file);
}
ostream& print(ostream &os, const QueryResult &qr){
os <<"The result of your query "<< qr.sought <<" is: \n";
for (const auto &index: *qr.sp_lines){
os << "\t(line " << index + 1 << ")";
const StrBlobPtr wp(qr.file, index);
os << wp.deref() << "\n";
}
return os;
}
int main(){
/*15.1*/
#ifdef NUM151
cout << "基类的虚成员希望派生类定义自己的版本。一般的基类都会定义虚函数,即使不做任何实际操作。"<<endl;
#endif
/*15.2*/
#ifdef NUM152
cout<<"private成员允许基类和友元类访问,派生类不能访问;派生类有权访问protested成员,但是禁止其他成员访问,"<<endl;
#endif
/*15.3*/
#ifdef NUM153
Quote basic( "a0-201-54848-8", 45);
print_total(cout, basic, 20);
#endif
/*15.4*/
#ifdef NUM154
cout << "(a)不正确,从自己本身派生. (b)这是定义而非声明. (c)派生类的声明包含派生名但不包含派生列表."<<endl;
#endif
/*15.5*/
#ifdef NUM155
cout <<"见Chapter15.h"<<endl;
#endif
/*15.6*/
#ifdef NUM156
Quote basic( "0-201-54848-8", 45);
print_total(cout, basic, 20);
Bulk_quote bulk("0-201-54848-8", 45, 15, 0.2);
print_total(cout, bulk, 20);
#endif
/*15.7*/
#ifdef NUM157
Quote basic( "0-201-54848-8", 45);
print_total(cout, basic, 30);
Limit_quote lq("0-201-54848-8", 45, 20, 0.2);
print_total(cout, lq, 30);
#endif
/*15.15*/
#ifdef NUM158
cout << "静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型."
"动态类型则是表达式表示的内存中的对象的类型.动态类型直到运行时才可知."<<endl;
#endif
/*15.9*/
#ifdef NUM159
cout <<"指向基类的指针和引用的静态类型都不同于它的动态类型。"<<endl;
#endif
/*15.10*/
#ifdef NUM1510
cout<<"因为ifstream继承于istream, 所以我们能够像使用cin一样使用ifstream对象."<<endl;
#endif
/*15.11*/
#ifdef NUM1511
Quote q("aaa", 10.60);
Bulk_quote bq("bbb",111, 10, 0.3);
Limit_quote lq("ccc",222,10,0.3);
/** @note Not dynamic binding!
编译器在编译阶段就已经直到r所指的对象。结果时基类对象的虚函数被调用.
*/
Quote& r = q;
r.debug();
cout << "\n";
r = bq;
r.debug();
cout << "\n";
r = lq;
r.debug();
cout << "\n";
cout << "====================\n";
/** @note dynamic binding!
print_debug编译时,编译器单独对每一个进行编译。编译器并不知道参数q所指向的对象。
由此,编译器将这一判断留到运行时,即动态绑定。
一旦动态绑定进行,派生类中对应的虚函数被调用。
*/
print_debug(q);
cout << "\n";
print_debug(lq);
cout << "\n";
print_debug(bq);
cout << "\n";
#endif
/*15.12*/
#ifdef NUM1512
cout <<"可以,override意味着从写在基类中同名的虚函数。final意味着防止派生类重写虚函数。"<<endl;
#endif
/*15.13*/
#ifdef NUM1513
cout << "派生类中的print有问题,试图调用基类的print函数,但是省略了作用域符号::,所以将陷入无限递归."
"修改见Chapter15.h"<<endl;
derived de;
de.print(cout);
#endif
/*15.14*/
#ifdef NUM1514
base bobj;
base *bp1 = &bobj;
base &br1 = bobj;
derived dobj;
base *bp2 = &dobj;
base &br2 = dobj;
//! a. this is an object, so compile time.
//bobj.print(cout);
//! b. this is an object, so compile time.
//dobj.print(cout);
//! c. function name is not virtual , so no dynamic
//! binding happens.so conpile time
//cout << bp1->name();
//! d. function name is not virtual , so no dynamic
//! binding happens.so conpile time
//cout << bp2->name();
//! e. run time
//br1.print(cout);
//! f. run time
br2.print(cout);
#endif
/*15.15*/
#ifdef NUM1515
cout <<"见Chapter15.h"<<endl;
#endif
/*15.16*/
#ifdef NUM1516
Limit_quote_16 lq("0-201-54848-8", 45, 20, 0.2);
print_total(cout, lq, 20);
#endif
/*15.17*/
#ifdef NUM1517
Disc_quote d;
#endif
/*15.18*/
#ifdef NUM1518
Pub_Derv d1;
Base *p = &d1; //合法
Priv_Derv d2;
//p = &d2; //不合法
Prot_Derv d3;
//p = &d3; //不合法
Derived_from_Public dd1;
p = &dd1; //合法
Derived_from_Private dd2;
//p =& dd2; //不合法
Derived_from_Protected dd3;
//p = &dd3; //不合法
cout << "只有第1、4是正确的,在用户代码中,派生类向基类的转换(指针)"
"只有当D公有的继承B时,用户代码才能使用派生类向基类转换."<<endl;
#endif
/*15.19*/
#ifdef NUM1519
cout << "因为转换发生在函数中,关于书中的第二条规则: "
"不论D以什么方式继承B,D的成员函数和友元函数都能使用派生类向基类的转换. 所以:"
"所有继承于Base的类Pub_Derv, Priv_Derv, Prot_Derv都可以转换."
"Derived_from_Public, Derived_from_Protected这两个类也可以转换两次得到."<<endl;
#endif
/*15.20*/
#ifdef NUM1520
cout << "见15.18"<<endl;
#endif
/*15.21*/
#ifdef NUM1521
cout <<"见Chapter15.h"<<endl;
#endif
/*15.23*/
#ifdef NUM1523
Base_23 bobj;
D1 d1obj;
D2 d2obj;
Base_23 *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn(); //虚调用,将在运行时调用Base_32::fcn()
bp2->fcn(); //虚调用,运行时调用D1::fcn
bp3->fcn(); //虚调用, 运行时调用 D2::fcn
D1 *d1p = &d1obj; D2 *d2p = &d2obj;
//bp2->f2(); //Base_32没有f2()的成员
d1p->f2(); //虚调用,运行时调用 D1::f2()
d2p->f2(); //虚调用,运行时调用 D2::f2()
#endif
/*15.24*/
#ifdef NUM1524
cout<<"基类需要虚析构函数,在这些类完成动态绑定的同时,通过调用正确的析构函数来执行销毁操作."<<endl;
#endif
/*15.25*/
#ifdef NUM1525
cout <<"会产生编译错误"
"因为Bulk_quote中也有默认构造函数,而这个构造函数的状态是由Disc_quote的默认构造函数决定的;"
"Bulk_quote删除了默认构造函数,同时存在另一个4个参数的构造函数,所以编译器也不会再合成默认构造函数"<<endl;
#endif
/*15.26*/
#ifdef NUM1526
Bulk_quote bq1;
Bulk_quote bq2("0-201-82470-1", 45, 3, 0.3);
bq2 = move(bq2);
print_total(cout, bq2, 20);
#endif
/*15.27*/
#ifdef NUM1527
Bulk_quote bq("sss",20.0,2,0.3);
#endif
/*15.218*/
#ifdef NUM1528
/**
* outcome == 9090
*/
vector<Quote> v;
for(unsigned i =1; i != 10; ++i)
v.push_back(Bulk_quote_27("sss", i * 10.1, 10, 0.3));
double total = 0;
for (const auto& b : v)
{
total += b.net_price(20);
}
cout << total << endl;
cout << "======================\n\n";
/**
* outccome == 6363
*/
vector<shared_ptr<Quote>> pv; //多态
for(unsigned i =1; i != 10; ++i)
pv.push_back(make_shared<Bulk_quote_27>(Bulk_quote_27("sss", i * 10.1, 10, 0.3)));
double total_p = 0;
for (auto p : pv)
{
total_p += p->net_price(20);
print_total(cout, *p, 20);
}
cout << total_p << endl;
#endif
/*15.29*/
#ifdef NUM1529
//答案不一样 因为v保存的全部是Quote的对象(静态类型) 也就是说Bulk_quote强行转换成了Quote
//而pv保存的是(动态类型) 转换的仅是指针
#endif
/*15.30*/
#ifdef NUM1530
Basket basket;
for (unsigned i = 0; i != 10; ++i)
// basket.add_item(Bulk_quote_27("Bible",20.6,20,0.3));
basket.add_item(make_shared<Bulk_quote_27>("Bible",20.6,20,0.3));
for (unsigned i = 0; i != 10; ++i)
// basket.add_item(Bulk_quote_27("C++Primer",30.9,5,0.4));
basket.add_item(make_shared<Bulk_quote_27>("C++Primer",30.9,5,0.4));
for (unsigned i = 0; i != 10; ++i)
// basket.add_item(Quote("CLRS",40.1));
basket.add_item(make_shared<Quote>("CLRS",40.1));
ofstream log("log.txt",ios_base::app|ios_base::out);
basket.total_receipt(log);
#endif
/*15.31*/
#ifdef NUM1531
//! (a) Query(s1) | Query(s2) & ~ Query(s3);
// OrQuery, AndQuery, NotQuery, WordQuery
//! (b) Query(s1) | (Query(s2) & ~ Query(s3));
// OrQuery, AndQuery, NotQuery, WordQuery
//! (c) (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4)));
//! OrQuery, AndQuery, WordQuery
#endif
/*15.32*/
#ifdef NUM1532
cout<<"拷贝,移动,赋值和销毁使用的都是编译器合成的版本。"
"拷贝,赋值:新对象的智能指针q将指向原对象中q指向的Query_base体系下的对象,"
"所以成员智能指针的use count加1;"
"移动:原对象的智能指针q指向空的nullptr,新对象指向原对象中成员的q指向的对象。use count不变。"
"销毁:对象成员智能指针q的use count -1,如果use count为0,则其对象就自动销毁。"<<endl;
#endif
/*15.33*/
#ifdef NUM1533
cout<<"Query_base是一个纯虚类,没有自己的对象."<<endl;
#endif
/*15.34*/
#ifdef NUM1534
//(a)
// Query q = Query("fiery") & Query("bird") | Query("wind");
// 1. Query::Query(const string &s) 3次
// 2. WordQuery::WordQuery(const string& s) 3次
// 3. AndQuery::AndQuery(const Query &left, const Query &right);
// 4. BinaryQuery(const Query &1, const BinaryQuery &r, string s);
// 5. Query::Query(shared_ptr<Query_base> query) 2次
// 6. OrQuery::OrQuery(const Query& left, const Query &right);
// 7. BinaryQuery(const Query &l, const Query &r, string s);
// 8. Query::Query(shared_ptr<Query_base> query); 2次
//(b)
//BinaryQuery
//BinaryQuery
//Query
//WordQuery
//Query
//WordQuery
//Query
//WordQuery
//(c)
//Query::eval()
//Query_base的派生类的各种eval
#endif
/*15.35*/
#ifdef NUM1535
cout <<"见Chapter15.h"<<endl;
#endif
/*15.36*/
#ifdef NUM1536
ifstream file("test.txt");
TextQuery tQuery(file);
Query q = Query("wind");
cout << q.eval(tQuery);
#endif
/*15.37*/
#ifdef NUM1537
cout <<"不需要改变"<<endl;
#endif
/*15.38*/
#ifdef NUM1538
BinaryQuery a = Query("fiery") & Query("bird");
//不合法 纯虚类没有对象
AndQuery b = Query("fiery") & Query("bird");
//不合法 &操作后返回的是Query的对象 无法转换为AndQuery的对象
OrQuery c = Query("fiery") & Query("bird");
//不合法 &操作后返回的是Query的对象 无法转换为OrQuery的对象
#endif
/*15.39*/
#ifdef NUM1539
ifstream file("test.txt");
TextQuery tQuery(file);
Query q = Query("fieryzzz") | Query("wind");
cout << q.eval(tQuery);
#endif
/*15.40*/
#ifdef NUM1540
cout <<"不会发生什么,因为make_shared会自动分配一个新的set。"<<endl;
#endif
/*15.42*/
#ifdef NUM1542
//引入一个历史系统,用户通过编号查询之前的查询操作,可以增加内容或者将其与其他查询组合
ifstream fin("test.txt");
TextQuery text(fin);
QueryHistory history;
Query q0("Alice");
Query q1("hair");
Query q2("Daddy");
history.add_query(q0);
history.add_query(q1);
history[0] = history[0] | q2;
auto result = history[0].eval(text);
print(cout, result);
#endif
return 0;
}
参考资料:
c++ primer中文版第五版,电子工业出版社。
c++ primer第四版习题解答,人民邮电出版社。
pezy@githubhttps://github.com/pezy/CppPrimer