天天看點

C++Primer第五版 習題答案 第十五章 面向對象程式設計(Object-Oriented Programming)

練習15.1

什麼是虛成員?

虛函數是基類希望其派生類進行覆寫的函數。

練習15.2

protected 通路說明符與 private 有何差別?

派生類的成員函數可以通路protected通路運算符的成員,而不能通路private的。

練習15.3

定義你自己的 Quote 類和 print_total 函數。

Quote_ex03.h

#ifndef QUOTE_H_
#define QUOTE_H_

#include <string>

class Quote
{
public:
	Quote() = default;
	Quote(const std::string &book, double sales_price) : bookNo(book), price(sales_price) {}
	std::string isbn() const { return bookNo; }
	virtual double net_price(std::size_t n) const { return n * price; } 
	virtual ~Quote() = default;
private:
	std::string bookNo;
protected:
	double price = 0;
};

#endif
           

ex03.cpp

#include "Quote_ex03.h"
#include <iostream>
#include <functional>

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

int main()
{
	Quote q("A1-001", 80);
	print_total(std::cout, q, 2);

	return 0;
}
           

練習15.4

下面哪條聲明語句是不正确的?請解釋原因。
class Base { ... };
(a) class Derived : public Derived { ... };
(b) class Derived : private Base { ... };
(c) class Derived : public Base;
           

(a)不正确,是定義,不是聲明,而且不能繼承它自己;

(b)不正确,是定義,不是聲明;

(c)不正确,聲明中包含類名,但是不包含它的派生清單。

練習15.05

定義你自己的 Bulk_quote 類。
#ifndef BULK_QUOTE_H_
#define BULK_QUOTE_H_

#include "Quote_ex03.h"
#include <string>

class Bulk_quote : public Quote
{
public:
	Bulk_quote() = default;
	Bulk_quote(const std::string &book, double p, std::size_t qty, double disc) : Quote(book, p), min_qty(qty), discount(disc) { }
	double net_price(std::size_t) const override;
private:
	std::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;
}

#endif
           

練習15.06

将 Quote 和 Bulk_quote 的對象傳給15.2.1節練習中的 print_total 函數,檢查該函數是否正确。
#include "Quote_ex03.h"
#include "Bulk_quote_ex05.h"
#include <iostream>
#include <functional>

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

int main()
{
	Quote q("A1-001", 80);
	Bulk_quote bq("A1-001", 80, 5, 0.2);
	print_total(std::cout, q, 10);
	print_total(std::cout, bq, 10);

	return 0;
}
           

練習15.07

定義一個類使其實作一種數量受限的折扣政策,具體政策是:當購買書籍的數量不超過一個給定的限量時享受折扣,如果購買量一旦超過了限量,則超出的部分将以原價銷售。

Limit_quote_ex07.h

#ifndef LIMIT_QUOTE_H_
#define LIMIT_QUOTE_H_

#include "Quote_ex03.h"
#include <string>

class Limit_quote : public Quote
{
public:
	Limit_quote() = default;
	Limit_quote(const std::string &book, double p, std::size_t qty, double disc) : Quote(book, p), max_qty(qty), discount(disc) { }
	double net_price(std::size_t) const override;
private:
	std::size_t max_qty = 0;
	double discount = 0.0;
};

double Limit_quote::net_price(size_t cnt) const
{
	if(cnt > max_qty) return max_qty * (1 - discount) * price + (cnt - max_qty)*price;
	else return cnt * price;
}

#endif
           

ex07.cpp

#include "Quote_ex03.h"
#include "Limit_quote_ex07.h"
#include <iostream>
#include <functional>

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

int main()
{
	Quote q("A1-001", 80);
	Limit_quote bq("A1-001", 80, 5, 0.2);
	print_total(std::cout, q, 10);
	print_total(std::cout, bq, 10);

	return 0;
}
           

練習15.08

給出靜态類型和動态類型的定義。

表達式的靜态類型在編譯時總是已知的,它是變量聲明時的類型或表達式生成的類型;

動态類型則是變量或表達式表示的記憶體中的對象類型。動态類型直到運作時才可知。

練習15.09

在什麼情況下表達式的靜态類型可能與動态類型不同?請給出三個靜态類型與動态類型不同的例子。

摘自github。

Bulk_quote bulk_quote("bulk_quote_1", 10.10, 10, 0.5);

    // The pointer is of static type Quote, but it's dynamic type is Bulk Quote
    // Because of polymorphism the Bulk Quote implementation of the net_price()
    // method gets called.
    Quote *quote_pointer = &bulk_quote;
    quote_pointer->net_price(5);

    // The reference is of static type Quote, but it's dynamic type is Bulk Quote
    // Like with the pointer, the Bulk Quote implementation of the net_price()
    // method gets called.
    Quote &quote_reference = bulk_quote;
    quote_reference.net_price(5);

    // The static type of this variable is Quote. Here the Bulk Quote object
    // gets upcasted. The Quote part of bulk_quote gets copied into quote, but
    // the rest is not handled. Because of the cast the Quote implementation of
    // the net_price() method gets called.
    Quote quote = bulk_quote;
    quote.net_price(5);
           

練習15.10

回憶我們在8.1節進行的讨論,解釋第284頁中将 ifstream 傳遞給 Sales_data 的read 函數的程式是如何工作的。

read中std::istream,std::ifstream是繼承自std::istream,是以,std::istream的執行個體傳入後會轉換為std::istream。

練習15.11

為你的 Quote 類體系添加一個名為 debug 的虛函數,令其分别顯示每個類的資料成員。

Quote_ex11.h

#ifndef QUOTE_H_
#define QUOTE_H_

#include <string>
#include <iostream>

class Quote
{
public:
	Quote() = default;
	Quote(const std::string &book, double sales_price) : bookNo(book), price(sales_price) {}
	std::string isbn() const { return bookNo; }
	virtual double net_price(std::size_t n) const { return n * price; }
	virtual void debug() const;
	virtual ~Quote() = default;
private:
	std::string bookNo;
protected:
	double price = 0;
};

void Quote::debug() const
{
	std::cout << "bookNo: " << bookNo 
	<< "; price: " << price;
}

#endif
           

Bulk_quote_ex11.h

#ifndef BULK_QUOTE_H_
#define BULK_QUOTE_H_

#include "Quote_ex11.h"
#include <string>
#include <iostream>

class Bulk_quote : public Quote
{
public:
	Bulk_quote() = default;
	Bulk_quote(const std::string &book, double p, std::size_t qty, double disc) : Quote(book, p), min_qty(qty), discount(disc) { }
	double net_price(std::size_t) const override;
	void debug() const override;
private:
	std::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;
}

void Bulk_quote::debug() const
{
	Quote::debug();
	std::cout << "; min_qty: " << min_qty
	<< "; discount: " << discount;
}

#endif
           

ex11.cpp

#include "Quote_ex11.h"
#include "Bulk_quote_ex11.h"
#include <iostream>
#include <functional>

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

int main()
{
	Quote q("A1-001", 80);
	Bulk_quote bq("A1-001", 80, 5, 0.2);
	// print_total(std::cout, q, 10);
	// print_total(std::cout, bq, 10);

	q.debug();
	std::cout << std::endl;
	bq.debug();
	std::cout << std::endl;

	return 0;
}
           

練習15.12

有必要将一個成員函數同時聲明成 override 和 final 嗎?為什麼?

有必要,如果該成員函數不希望被後續的派生類繼承,則可聲明成final,與目前的override沒有關系。

練習15.13

給定下面的類,解釋每個 print 函數的機理:
class base {
public:
	string name() { return basename;}
	virtual void print(ostream &os) { os << basename; }
private:
	string basename;
};
class derived : public base {
public:
	void print(ostream &os) { print(os); os << " " << i; }
private:
	int i;
};
           

在上述代碼中存在問題嗎?如果有,你該如何修改它?

派生類中的print函數會遞歸調用目前print,知道記憶體耗盡。

練習15.14

給定上一題中的類以及下面這些對象,說明在運作時調用哪個函數:
base bobj; 		base *bp1 = &bobj; 	base &br1 = bobj;
derived dobj; 	base *bp2 = &dobj; 	base &br2 = dobj;
(a) bobj.print();	(b)dobj.print();	(c)bp1->name();
(d)bp2->name();		(e)br1.print();		(f)br2.print();
           

(a)調用base中的print,編譯時确定;

(b)調用derived中的print,編譯時确定;

(c)調用base中的name函數,不是虛函數,沒有動态綁定,是以編譯時确定;

(d)調用derived中的name函數,不是虛函數,沒有動态綁定,是以編譯時确定;

(e)調用base中的print,運作時确定;

(f)調用derived中的print,運作時确定。

練習15.15

定義你自己的 Disc_quote 和 Bulk_quote。

Disc_quote_ex15.h

#ifndef DISC_QUOTE_
#define DISC_QUOTE_

#include "Quote_ex11.h"
#include <string>

class Disc_quote : public Quote
{
public:
	Disc_quote() = default;
	Disc_quote(const std::string &book, double price, std::size_t qty, double disc)
	: Quote(book, price), quantity(qty), discount(disc) { }
	double net_price(std::size_t) const = 0;
protected:
	std::size_t quantity = 0;
	double discount = 0.0;
};


#endif
           

Bulk_quote_ex15.h

#ifndef BULK_QUOTE_H_
#define BULK_QUOTE_H_

#include "Disc_quote_ex15.h"
#include <string>
#include <iostream>

class Bulk_quote : public Disc_quote
{
public:
	Bulk_quote() = default;
	Bulk_quote(const std::string &book, double price, std::size_t qty, double disc) : Disc_quote(book, price, qty, disc) { }
	double net_price(std::size_t) const override;
	void debug() const override;
};

double Bulk_quote::net_price(size_t cnt) const
{
	if(cnt >= quantity) return cnt * (1 - discount) * price;
	else return cnt * price;
}

void Bulk_quote::debug() const
{
	Quote::debug();
	std::cout << "; quantity: " << quantity
	<< "; discount: " << discount;
}

#endif
           

ex15.cpp

#include "Quote_ex11.h"
#include "Bulk_quote_ex15.h"
#include <iostream>
#include <functional>

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

int main()
{
	Quote q("A1-001", 80);
	Bulk_quote bq("A1-001", 80, 5, 0.2);
	// print_total(std::cout, q, 10);
	// print_total(std::cout, bq, 10);

	q.debug();
	std::cout << std::endl;
	bq.debug();
	std::cout << std::endl;

	return 0;
}
           

練習15.16

改寫你在15.2.2節練習中編寫的數量受限的折扣政策,令其繼承 Disc_quote。

Limit_quote_ex16.h

#ifndef LIMIT_QUOTE_H_
#define LIMIT_QUOTE_H_

#include "Disc_quote_ex15.h"
#include <string>
#include <iostream>

class Limit_quote : public Disc_quote
{
public:
	Limit_quote() = default;
	Limit_quote(const std::string &book, double price, std::size_t qty, double disc) : Disc_quote(book, price, qty, disc) { }
	double net_price(std::size_t) const override;
	void debug() const override;
};

double Limit_quote::net_price(size_t cnt) const
{
	if(cnt > quantity) return quantity * (1 - discount) * price + (cnt - quantity)*price;
	else return cnt * price;
}

void Limit_quote::debug() const
{
	Quote::debug();
	std::cout << "; quantity: " << quantity
	<< "; discount: " << discount;
}

#endif
           

ex16.cpp

#include "Quote_ex11.h"
#include "Limit_quote_ex16.h"
#include <iostream>
#include <functional>

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

int main()
{
	Quote q("A1-001", 80);
	Limit_quote bq("A1-001", 80, 5, 0.2);
	print_total(std::cout, q, 10);
	print_total(std::cout, bq, 10);

	// q.debug();
	// std::cout << std::endl;
	// bq.debug();
	// std::cout << std::endl;

	return 0;
}
           

練習15.17

嘗試定義一個 Disc_quote 的對象,看看編譯器給出的錯誤資訊是什麼?
$ g++ -o ex17 ex17.cpp -std=c++11
ex17.cpp: In function ‘int main()’:
ex17.cpp:17:13: error: cannot declare variable ‘dq’ to be of abstract type ‘Disc_quote’
  Disc_quote dq("A1-001", 80, 5, 0.2);
             ^
In file included from Limit_quote_ex16.h:4:0,
                 from ex17.cpp:2:
Disc_quote_ex15.h:7:7: note:   because the following virtual functions are pure within ‘Disc_quote’:
 class Disc_quote : public Quote
       ^
Disc_quote_ex15.h:13:9: note: 	virtual double Disc_quote::net_price(std::size_t) const
  double net_price(std::size_t) const = 0;
         ^
           

練習15.18

假設給定了第543頁和第544頁的類,同時已知每個對象的類型如注釋所示,判斷下面的哪些指派語句是合法的。解釋那些不合法的語句為什麼不被允許:
Base *p = &d1;  //d1 的類型是 Pub_Derv
p = &d2;		//d2 的類型是 Priv_Derv
p = &d3;		//d3 的類型是 Prot_Derv
p = &dd1;		//dd1 的類型是 Derived_from_Public	
p = &dd2;		//dd2 的類型是 Derived_from_Private
p = &dd3;		//dd3 的類型是 Derived_from_Protected
           

(a)合法;

(b)非法;

©非法;

(d)合法;

(d)非法;

(f)非法。

隻有當D公有地繼承B時,使用者代碼才能使用派生類向基類的轉換。

練習15.19

假設543頁和544頁的每個類都有如下形式的成員函數:

對于每個類,分别判斷上面的函數是否合法。

Base類:合法;

Pub_Derv類:合法;

Priv_Derv類:合法;

Prot_Derv類:合法;

Derived_from_Public類:合法;

Derived_from_Private類:非法;

Derived_from_Protected類:合法。

練習15.20

編寫代碼檢驗你對前面兩題的回答是否正确。
class Base
{
public:
	void memfcn(Base &b) { b = *this; };
protected:
	int prot_mem;
private:
	char priv_mem;
};

struct Pub_Derv : public Base
{
public:
	void memfcn(Base &b) { b = *this; };
};

struct Priv_Derv : private Base
{
public:
	void memfcn(Base &b) { b = *this; };
};

struct Prot_Derv : protected Base
{
public:
	void memfcn(Base &b) { b = *this; };
};

struct Derived_from_Public : public Pub_Derv
{
public:
	void memfcn(Base &b) { b = *this; };
};

struct Derived_from_Private : public Priv_Derv
{
public:
	// void memfcn(Base &b) { b = *this; };
};

struct Derived_from_Protected : public Prot_Derv
{
public:
	void memfcn(Base &b) { b = *this; };
};

int main()
{
	Pub_Derv d1;
	Priv_Derv d2;
	Prot_Derv d3;
	Derived_from_Public dd1;
	Derived_from_Private dd2;
	Derived_from_Protected dd3;

	Base *p = &d1;
	// p = &d2;
	// p = &d3;
	p = &dd1;
	// p = &dd2;
	// p = &dd3;

	return 0;
}
           

練習15.21

從下面這些一般性抽象概念中任選一個(或者選一個你自己的),将其對應的一組類型組織成一個繼承體系:
(a) 圖形檔案格式(如gif、tiff、jpeg、bmp)
(b) 圖形基元(如方格、圓、球、圓錐)
(c) C++語言中的類型(如類、函數、成員函數)
           

代碼取自github:

#include <iostream>
#include <string>

// just for 2D shape
class Shape
{
public:
    typedef std::pair<double, double>    Coordinate;

    Shape() = default;
    Shape(const std::string& n) :
        name(n) { }

    virtual double area()       const = 0;
    virtual double perimeter()  const = 0;

    virtual ~Shape() = default;
private:
    std::string name;
};

class Rectangle : public Shape
{
public:
    Rectangle() = default;
    Rectangle(const std::string& n,
              const Coordinate& a,
              const Coordinate& b,
              const Coordinate& c,
              const Coordinate& d) :
        Shape(n), a(a), b(b), c(c), d(d) { }

    ~Rectangle() = default;

protected:
    Coordinate  a;
    Coordinate  b;
    Coordinate  c;
    Coordinate  d;
};

class Square : public Rectangle
{
public:
    Square() = default;
    Square(const std::string& n,
           const Coordinate& a,
           const Coordinate& b,
           const Coordinate& c,
           const Coordinate& d) :
        Rectangle(n, a, b, c, d) { }

    ~Square() = default;
};


int main()
{

    return 0;
}
           

練習15.22

對于你在上一題中選擇的類,為其添加函數的虛函數及公有成員和受保護的成員。

參見15.21。

練習15.23

假設第550頁的 D1 類需要覆寫它繼承而來的 fcn 函數,你應該如何對其進行修改?如果你修改之後 fcn 比對了 Base 中的定義,則該節的那些調用語句将如何解析?

添加 int fcn() override;

bp1->fcn(); //虛調用,将在運作時調用Base::fcn

bp2->fcn(); //虛調用,将在運作時調用D1::fcn

bp3->fcn(); //虛調用,将在運作時調用D2::fcn

練習15.24

哪種類需要虛析構函數?虛析構函數必須執行什麼樣的操作?

基類需要虛析構函數。如果一個類定義了析構函數,即使它通過=default的形式使用了合成的版本,編譯器也不會為這個類合成移動操作。

練習15.25

我們為什麼為 Disc_quote 定義一個預設構造函數?如果去掉該構造函數的話會對 Bulk_quote 的行為産生什麼影響?

已經定義了一個構造函數,預設構造函數是被删除的,需要顯式地定義。

如果去掉了Disc_quote的預設構造函數,Bulk_quote的預設構造函數是被删除的。

練習15.26

定義 Quote 和 Bulk_quote 的拷貝控制成員,令其與合成的版本行為一緻。為這些成員以及其他構造函數添加列印狀态的語句,使得我們能夠知道正在運作哪個程式。使用這些類編寫程式,預測程式将建立和銷毀哪些對象。重複實驗,不斷比較你的預測和實際輸出結果是否相同,直到預測完全準确再結束。

Quote_ex26.h

#ifndef QUOTE_H_
#define QUOTE_H_

#include <string>
#include <iostream>

class Quote
{
friend bool operator!=(const Quote &lhs, const Quote &rhs) { return lhs.bookNo != rhs.bookNo && lhs.price != rhs.price; }
public:
	Quote() = default;
	Quote(const std::string &book, double sales_price) : bookNo(book), price(sales_price) {}
	Quote(const Quote&);
	Quote(Quote&&) noexcept;
	Quote& operator=(const Quote&);
	Quote& operator=(Quote&&) noexcept;
	std::string isbn() const { return bookNo; }
	virtual double net_price(std::size_t n) const { return n * price; }
	virtual void debug() const;
	virtual ~Quote();
private:
	std::string bookNo;
protected:
	double price = 0;
};

#endif
           

Bulk_quote_ex26.h

#ifndef BULK_QUOTE_H_
#define BULK_QUOTE_H_

#include "Disc_quote_ex26.h"
#include <string>
#include <iostream>

class Bulk_quote : public Disc_quote
{
public:
	Bulk_quote() = default;
	Bulk_quote(const std::string &book, double price, std::size_t qty, double disc) : Disc_quote(book, price, qty, disc) { }
	Bulk_quote(Bulk_quote&);
	Bulk_quote(Bulk_quote&&) noexcept;
	Bulk_quote& operator=(Bulk_quote&);
	Bulk_quote& operator=(Bulk_quote&&) noexcept;
	double net_price(std::size_t) const override;
	void debug() const override;
	~Bulk_quote() override;
};

#endif
           

練習15.27

重新定義你的 Bulk_quote 類,令其繼承構造函數。

Bulk_quote.h

#ifndef BULK_QUOTE_H_
#define BULK_QUOTE_H_

#include "Disc_quote.h"
#include <string>
#include <iostream>

class Bulk_quote : public Disc_quote
{
public:
	Bulk_quote() = default;
	using Disc_quote::Disc_quote;
	Bulk_quote(Bulk_quote&);
	Bulk_quote(Bulk_quote&&) noexcept;
	Bulk_quote& operator=(Bulk_quote&);
	Bulk_quote& operator=(Bulk_quote&&) noexcept;
	double net_price(std::size_t) const override;
	void debug() const override;
	~Bulk_quote() override;
};

#endif
           

練習15.28

定義一個存放 Quote 對象的 vector,将 Bulk_quote 對象傳入其中。計算 vector 中所有元素總的 net_price。

ex28.cpp

#include "Quote.h"
#include "Bulk_quote.h"
#include "Limit_quote.h"
#include <iostream>
#include <functional>
#include <vector>
#include <memory>

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

int main()
{
	Quote q("A1-001", 80);
	Bulk_quote bq("A1-001", 80, 5, 0.2);
	Limit_quote lq("A1-001", 80, 5, 0.2);
	print_total(std::cout, q, 10);
	print_total(std::cout, bq, 10);
	print_total(std::cout, lq, 10);

	double total_price = 0;
	std::vector<Quote> vector_quote;
	vector_quote.push_back(bq);
	for(const auto &q : vector_quote)
		total_price += q.net_price(10);
	std::cout << "total_price:" << total_price << std::endl;

	return 0;
}
           

練習15.29

再運作一次你的程式,這次傳入 Quote 對象的 shared_ptr 。如果這次計算出的總額與之前的不一緻,解釋為什麼;如果一直,也請說明原因。

當派生類對象被指派給基類對象時,其中的派生類部分将被“切掉”,是以容器和存在繼承關系的類型無法相容。

而使用指針時,調用的是net_price版本依賴于指針所指對象的動态類型。

ex29.cpp

#include "Quote.h"
#include "Bulk_quote.h"
#include "Limit_quote.h"
#include <iostream>
#include <functional>
#include <vector>
#include <memory>

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

int main()
{
	Quote q("A1-001", 80);
	Bulk_quote bq("A1-001", 80, 5, 0.2);
	Limit_quote lq("A1-001", 80, 5, 0.2);
	print_total(std::cout, q, 10);
	print_total(std::cout, bq, 10);
	print_total(std::cout, lq, 10);

	std::vector<std::shared_ptr<Quote>> basket;
	// basket.push_back(std::make_shared<Quote>(q));
	basket.push_back(std::make_shared<Bulk_quote>(bq));
	// basket.push_back(std::make_shared<Limit_quote>(lq));

	double total_price = 0;
	for (const auto &vq : basket)
		total_price += vq->net_price(10);
	std::cout << "total_price:" << total_price << std::endl;

	return 0;
}
           

練習15.30

編寫你自己的 Basket 類,用它計算上一個練習中交易記錄的總價格。

将print_total函數放入Quote.h。

Basket.h

#ifndef BASKET_H_
#define BASKET_H_

#include "Quote.h"
#include <iostream>
#include <memory>
#include <set>

class Basket
{
public:
	void add_item(const std::shared_ptr<Quote> &sale) { items.insert(sale); }
	void add_item(const Quote &sale) { items.insert(std::shared_ptr<Quote>(sale.clone())); }
	void add_item(Quote &&sale) { items.insert(std::shared_ptr<Quote>(std::move(sale).clone())); }
	double total_receipt(std::ostream&) const;
private:
	static bool compare(const std::shared_ptr<Quote> &lhs, const std::shared_ptr<Quote> &rhs) { return lhs->isbn() < rhs->isbn(); }
	std::multiset<std::shared_ptr<Quote>, decltype(compare) *> items{compare};
};

#endif
           

Basket.cpp

#include "Basket.h"

double Basket::total_receipt(std::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));
	}

	os << "Total Sale: " << sum << std::endl;
	return sum;
}
           

ex30.cpp

#include "Quote.h"
#include "Bulk_quote.h"
#include "Limit_quote.h"
#include "Basket.h"
#include <iostream>
#include <functional>
#include <vector>
#include <memory>

int main()
{
	Bulk_quote bq("A1-001", 80, 5, 0.2);
	print_total(std::cout, bq, 10);

	std::vector<std::shared_ptr<Quote>> basket;
	basket.push_back(std::make_shared<Bulk_quote>(bq));

	// double total_price = 0;
	// for (const auto &vq : basket)
	// 	total_price += vq->net_price(10);
	// std::cout << "total_price:" << total_price << std::endl;

	Basket basket_object;
	for (int i = 0; i < 10; ++i)
	{
		basket_object.add_item(bq);
	}
	basket_object.total_receipt(std::cout);

	return 0;
}
           

練習15.31

已知 s1、s2、s3 和 s4 都是 string,判斷下面的表達式分别建立了什麼樣的對象:
(a) Query(s1) | Query(s2) & ~Query(s3);
(b) Query(s1) | (Query(s2) & ~Query(s3));
(c) (Query(s1) & (Query(s2)) | (Query(s3) & Query(s4)));
           

(a)WordQuery orQuery AndQuery NotQuery;

(b)WordQuery orQuery AndQuery NotQuery;

(c)WordQuery orQuery AndQuery。

練習15.32

當一個 Query 類型的對象被拷貝、移動、指派或銷毀時,将分别發生什麼?

拷貝:将會調用合成的拷貝構造函數。它将資料成員複制到新對象中。資料成員是一個共享指針,複制時,對應的共享指針指向相同的位址,兩個共享指針的使用計數變為2。

移動:将會調用合成的移動構造函數。它将資料成員移動到新對象中。移動時,建立對象的共享指針将指向原始共享指針指向的位址。移動操作後,新對象中共享指針的使用計數為1,而來自原始對象的指針變為nullptr。

拷貝指派:将調用合成的拷貝指派函數。此操作與複制操作相同。

拷貝移動:将調用合成的移動指派函數。此操作與移動操作相同。

銷毀:将調用合成的析構函數。它将調用share_ptr的析構函數,減少計數次數。如果計數變為0,shared_ptr的析構函數将删除它所指向的資源。

練習15.33

當一個 Query_base 類型的對象被拷貝、移動指派或銷毀時,将分别發生什麼?

由合成的版本管理。Query_base是一個抽象類,是以這種類型的對象本質上是派生類的子對象。

練習15.34

針對圖15.3建構的表達式:
(a) 例舉出在處理表達式的過程中執行的所有構造函數。
(b) 例舉出 cout << q 所調用的 rep。
(c) 例舉出 q.eval() 所調用的 eval。
           

(a)Query(const std::string &s),s為fiery、bird、wind;

WordQuery(const std::string &s),s為fiery、bird、wind;

AndQuery(const Query &left, const Query &right);

BinaryQuery(const Query &l, const Query &r, std::string s);

Query(std::shared_ptr<Query_base> query);

OrQuery(const Query &left, const Query &right);

BinaryQuery(const Query &l, const Query &r, std::string s);

Query(std::shared_ptr<Query_base> query);

(b)query.rep();

q->rep(),指向OrQuery::rep();

Query::rep(),lhs為AndQuery::rep(),rhs為WordQuery::rep();

AndQuery::rep();

Query::rep();

(c)q->eval(),指向OrQuery::eval()。

練習15.35

實作 Query 類和 Query_base 類,其中需要定義rep 而無須定義 eval。

Query.h

#ifndef QUERY_H_
#define QUERY_H_

#include <string>
#include <iostream>
#include "Query_base.h"
#include "WordQuery.h"
#include "TextQuery.h"

class Query
{
	friend Query operator~(const Query&);
	friend Query operator|(const Query&, const Query&);
	friend Query operator&(const Query&, const Query&);
public:
	Query(const std::string&);
	QueryResult eval(const TextQuery &t) const { return q->eval(t); }
	std::string rep() const { return q->rep(); }
private:
	Query(std::shared_ptr<Query_base> query) : q(query) { }
	std::shared_ptr<Query_base> q;
};

std::ostream& operator<<(std::ostream &os, const Query &query)
{
	return os << query.rep();
}

inline Query::Query(const std::string &s) : q(new WordQuery(s)) { std::cout << "Query::Query(const std::string &s)" << std::endl; }

#endif
           

Query_base.h

#ifndef QUERY_BASE_H_
#define QUERY_BASE_H_

#include <string>
#include <iostream>
#include "TextQuery.h"
#include "Query.h"

class Query_base
{
	friend class Query;
protected:
	using line_no = TextQuery::line_no;
	virtual ~Query_base() = default;
private:
	virtual QueryResult eval(const TextQuery&) const = 0;
	virtual std::string rep() const = 0;
};

#endif
           

練習15.36

在構造函數和 rep 成員中添加列印語句,運作你的代碼以檢驗你對本節第一個練習中(a)、(b)兩小題的回答是否正确。

(a)

WordQuery(const std::string &s)
Query::Query(const std::string &s)
WordQuery(const std::string &s)
Query::Query(const std::string &s)
WordQuery(const std::string &s)
Query::Query(const std::string &s)
BinaryQuery(const Query &l, const Query &r, std::string s)
AndQuery(const Query &left, const Query &right)
Query(std::shared_ptr<Query_base> query)
BinaryQuery(const Query &l, const Query &r, std::string s)
OrQuery(const Query &left, const Query &right)
Query(std::shared_ptr<Query_base> query)
           

(b)

Query::rep()
BinaryQuery::rep()|
Query::rep()
WordQuery::rep()
Query::rep()
BinaryQuery::rep()&
Query::rep()
WordQuery::rep()
Query::rep()
WordQuery::rep()
           

練習15.37

如果在派生類中含有 shared_ptr<Query_base> 類型的成員而非 Query 類型的成員,則你的類需要做出怎樣的改變?

接口函數需要改變,需要将Query改為shared_ptr<Query_base>。

練習15.38

下面的聲明合法嗎?如果不合法,請解釋原因;如果合法,請指出該聲明的含義。
BinaryQuery a = Query("fiery") & Query("bird");
AndQuery b = Query("fiery") & Query("bird");
OrQuery c = Query("fiery") & Query("bird");
           

(a)非法,BinaryQuery為抽象類;

(b)非法,傳回的為Query類型,不能轉換為AndQuery;

(c)非法,傳回的為Query類型,不能轉換為AndQuery。

練習15.39

實作 Query 類和 Query_base 類,求圖15.3中表達式的值并列印相關資訊,驗證你的程式是否正确。

傳回的是shared_ptr,需要增加構造函數或者修改get_file()。

Query類和Query_base類都沒有變化,運作結果如下:

((fiery & bird) | wind) occurs 3 
	(line 2) Her Daddy says when the wind blows 
	(line 4) like a fiery bird in flight. 
	(line 5) A beautiful fiery bird, he tells her, 
           

練習15.40

在 OrQuery 的 eval 函數中,如果 rhs 成員傳回的是空集将發生什麼?

不會發生什麼。

練習15.41

重新實作你的類,這次使用指向 Query_base 的内置指針而非 shared_ptr。請注意,做出上述改動後你的類将不能再使用合成的拷貝控制成員。

請自行實作。

練習15.42

從下面的幾種改進中選擇一種,設計并實作它:
(a) 按句子查詢并列印單詞,而不再是按行列印。
(b) 引入一個曆史系統,使用者可以按編号查閱之前的某個查詢,并可以在其中添加内容或者将其餘其他查詢組合。
(c) 允許使用者對結果做出限制,比如從給定範圍的行中跳出比對的進行顯示。
           

請自行實作。

繼續閱讀