筆記較爲零散,都是自己不熟悉的知識點。
習題答案至於一個.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