C++primer第十五章答案(第五版)
15.1
在C++语言中基类必须将它的两种成员函数区分开来:一种是基类希望其派生类进行覆盖的函数;一种是基类希望派生类直接继承而不要改变的函数。对于前者,基类通常将其定义为虚函数(virtual)。
15.2
protected访问说明符和private一样不能够在类外被调用,但是派生类的成员函数可以访问基类的protected成员,而不能访问基类的private成员。
15.3
#pragma once
#include <iostream>
using namespace std;
class Quote
{
public:
Quote() = default;
Quote(const string& book, double sales_price) :bookNo(book), price(sales_price) {};
string isbn()const { return bookNo; };
virtual double net_price(size_t n)const { return n * price; };
virtual ~Quote() = default;
private:
string bookNo;
protected:
double price = 0.0;
};
#include"Quote.h"
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
- 错误,不能够自己派生自己
- 正确
- 错误,声明不能包括派生列表
15.5
#include"Quote.h"
class Bulk_quote:public Quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& name, size_t n, double price, double disc) :Quote(name, price), min_qty(n), discount(disc) {};
double net_price(size_t n)const override;
private:
size_t min_qty = 0;
double discount = 0;
};
inline double Bulk_quote::net_price(size_t n)const
{
if (n > min_qty)
return n * price *(1- discount);
else
return n * price;
}
15.6
int main()
{
Quote q("012345", 30);
Bulk_quote b("012345", 30, 20, 0.2);
print_total(cout, q, 20);
print_total(cout, b, 20);
}
//输出
ISBN 012345 # sold: 20 total due: 600
ISBN 012345 # sold: 20 total due: 480
15.7
**#pragma once
#include"Quote.h"
class Limit_quote:public Quote
{
public:
Limit_quote() = default;
Limit_quote(const string & name, double price, size_t n, double disc) :Quote(name, price), max_qty(n), discount(disc) {};
double net_price(size_t n)const override;
private:
size_t max_qty=0;
double discount = 0;
};
inline double Limit_quote::net_price(size_t n)const
{
if (n >= max_qty)
return (n - max_qty) * price + max_qty * (1 - discount) * price;
else
return n * (1 - discount) * price;
}**
15.8
- 静态类型是在编译时就已经确定的,它是变量声明时的类型和表达式生成的类型。
- 动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知。
15.9
基类指针或引用的类型可能与其动态类型不一致,如果表达时既不是引用也不是指针,则他的动态类型永远与静态类型一致。
15.10
read函数的参数为istream&,fstream作为iostream的派生类,可以作为iostream引用的动态类型传入。
15.11
void Quote::Debug()const
{
cout << "This is Quote class" << endl;
cout << "bookNo=" << bookNo << " price=" << price << endl;
}
void Bulk_quote::Debug()const
{
cout << "This is Bulk_quote class" << endl;
cout << "bookNo=" << isbn() << " price=" << price << endl;
cout << "min_qty=" << min_qty << " discount=" << discount << endl;
}
void Limit_quote::Debug()const
{
cout << "This is Bulk_quote class" << endl;
cout << "bookNo=" << isbn() << " price=" << price << endl;
cout << "max_qty=" << max_qty << " discount=" << discount << endl;
}
15.12
有必要,两者并不冲突,override标识符是代表该函数重载基类的虚函数,方便编译器检测错误。而final标识符是禁止继承自己的子类重载该虚函数。
15.13
derived类的print函数中调用了自身,会导致无限递归。
将print(os)改为base::print(os),在加上override标识符,表示该函数重载基类虚函数。
15.14
- (a)调用base::print();
- (b)调用derived::print()
- (c)调用base::name()
- (d)调用base::name()
- (e)调用base::print();
- (f)调用derived::print()
15.15
#include"Quote.h"
class Disc_quote:public Quote
{
public:
Disc_quote() = default;
Disc_quote(string bookNo, double price, double disc, size_t q) :Quote(bookNo, price), discount(disc), qty(q) {};
double net_price(size_t)const = 0;
protected:
double discount;
size_t qty;
};
#include"Disc_quote.h"
class Bulk_quote:public Disc_quote
{
public:
Bulk_quote() = default;
Bulk_quote(const string& name, double price, size_t n, double disc) :Disc_quote(name, price,disc,n) {};
double net_price(size_t n)const override;
void Debug()const override;
};
inline double Bulk_quote::net_price(size_t n)const
{
if (n >= qty)
return n * price *(1- discount);
else
return n * price;
}
void Bulk_quote::Debug()const
{
cout << "This is Bulk_quote class" << endl;
cout << "bookNo=" << isbn() << " price=" << price << endl;
cout << "min_qty=" << qty << " discount=" << discount << endl;
}
15.16
#pragma once
#include"Disc_quote.h"
class Limit_quote:public Disc_quote
{
public:
Limit_quote() = default;
Limit_quote(const string & name, double price, size_t n, double disc) :Disc_quote(name, price, disc, n) {};
double net_price(size_t n)const override;
void Debug()const override;
private:
size_t max_qty=0;
double discount = 0;
};
inline double Limit_quote::net_price(size_t n)const
{
if (n >= max_qty)
return (n - max_qty) * price + max_qty * (1 - discount) * price;
else
return n * (1 - discount) * price;
}
void Limit_quote::Debug()const
{
cout << "This is Bulk_quote class" << endl;
cout << "bookNo=" << isbn() << " price=" << price << endl;
cout << "max_qty=" << max_qty << " discount=" << discount << endl;
}
15.17
不允许使用抽象类型“Disc_count”的对象,函数“Disc_quote::net_price”是纯虚函数。
15.18
如果是保护或私有继承,则派生类不能向基类转换
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,非法
15.19
- 不论D以什么方式继承B,D的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的类型转换对于派生类和友元来说是永远可访问的。因此,Pub_Derv, Pro_Derv和Priv_Derv类中都合法。
- 如果D继承B的方式是公有的或者受保护的,则D的派生类的成员和友元可以使用D向B的类型转换;反之,如果D继承B的方式是私有的,则不能使用。因此,Derived_from_Public合法,Derived_from_Private和Derived_from_Protected都不合法。
15.20
略
15.21
(b)图形基元
15.22
**#include <iostream>
using namespace std;
class Shape
{
public:
virtual double area ()const = 0;
virtual double perimiter ()const = 0;
virtual ~Shape() = default;
};
class Rectangle :public Shape
{
public:
Rectangle(double l, double w):length(l), width(w) {}
double area()const override
{
return length * width;
}
double perimiter()const override
{
return 2 * (length + width);
}
private:
double length;
double width;
};
class Circle :public Shape
{
public:
Circle(double r) :radius(r) {}
double area()const override
{
return radius * radius * 3.14;
}
double perimiter()const override
{
return 2 * radius * 3.14;
}
protected:
double radius;
};
class spshere:public Circle
{
public:
spshere(double r):Circle(r){}
double area()const override
{
return 4 * 3.14 * radius, radius;
}
double volume()const
{
return 3 / 4 * 3.14 * radius * radius * radius;
}
};
class Circle_Cone : public Circle
{
public:
double volume() const
{
return 3.14 * radius * radius * height;
}
private:
double height;
};**
15.23
int fcn()override{}//在D1类添加该成员函数,覆盖继承而来的fcn函数
bp2->fcn()//虚调用,调用D1::fcn。其他无变化
15.24
作为基类通常应该定义一个虚析构函数,通过将在基类中将析构函数定义为虚函数以确保执行正确的析构函数版本。
15.25
去掉该默认构造函数后,由于该类自定义了构造函数,如果不显示声明,默认构造函数将不会生成。这将阻止其子类生成默认构造函数。
15.26
class Quote
{
public:
Quote() = default;
Quote(const string& book, double sales_price) :bookNo(book), price(sales_price) {};
Quote(const Quote& q):price(q.price),bookNo(q.bookNo)
{
cout << "Call the copy construction of Quote." << endl;
}
Quote(Quote&& q) :price(move(q.price)), bookNo(move(q.bookNo))
{
cout << "Call the move construction of Quote" << endl;
}
Quote& operator=(const Quote& q)noexcept
{
bookNo = q.bookNo;
price = q.price;
cout << "Call the assginment construction of Quote" << endl;
return *this;
}
string isbn()const { return bookNo; };
virtual double net_price(size_t n)const { return n * price; };
virtual ~Quote() = default;
virtual void Debug()const;
private:
string bookNo;
protected:
double price = 0.0;
};
void Quote::Debug()const
{
cout << "This is Quote class" << endl;
cout << "bookNo=" << bookNo << " price=" << price << endl;
}
#include"Quote.h"
class Disc_quote:public Quote
{
public:
Disc_quote() = default;
Disc_quote(string bookNo, double price, size_t q,double disc) :Quote(bookNo, price), discount(disc), qty(q) {};
Disc_quote(const Disc_quote& dq) :Quote(dq), discount(dq.discount), qty(dq.qty)
{
cout << "Call the copy construction of Disc_quote." << endl;
}
Disc_quote(Disc_quote&& dq)noexcept:Quote(move(dq)),discount(dq.discount),qty(dq.qty)
{
cout << "Call the move construction of Disc_quote" << endl;
}
Disc_quote& operator=(const Disc_quote& dq)
{
Quote::operator=(dq);
discount = dq.discount;
qty = dq.qty;
cout << "Call the assginment construction of Disc_quote" << endl;
return *this;
}
double net_price(size_t)const = 0;
protected:
double discount;
size_t qty;
};
15.27
#include"Disc_quote.h"
class Bulk_quote:public Disc_quote
{
public:
Bulk_quote()
{
cout << "Bulk_quote default construction." << endl;
}
using Disc_quote::Disc_quote;
/* Bulk_quote(const std::string& b, double p, std::size_t q, double disc) : Disc_quote(b, p, q, disc) { }
Bulk_quote(Bulk_quote& bq) : Disc_quote(bq)
{
cout << "Call the copy construction of Bulk_quote." << endl;
}
Bulk_quote& operator=(Bulk_quote& rhs)
{
Disc_quote::operator=(rhs);
cout << "Call the assignment construction of Bulk_quote." << endl;
return *this;
}
Bulk_quote(Bulk_quote&& bq)noexcept : Disc_quote(move(bq))
{
cout << "Call the move construction of Bulk_quote." << endl;
}*/
double net_price(size_t n)const override;
void Debug()const override;
};
inline double Bulk_quote::net_price(size_t n)const
{
if (n >= qty)
return n * price *(1- discount);
else
return n * price;
}
void Bulk_quote::Debug()const
{
cout << "This is Bulk_quote class" << endl;
cout << "bookNo=" << isbn() << " price=" << price << endl;
cout << "min_qty=" << qty << " discount=" << discount << endl;
}
15.28
int main()
{
vector<Quote> basket;
basket.push_back(Bulk_quote("No-123", 30, 20, 0.2));
basket.push_back(Bulk_quote("No-123", 30, 20, 0.2));
basket.push_back(Bulk_quote("No-123", 30, 20, 0.2));
/* vector<shared_ptr<Quote>>basket;
basket.push_back(make_shared<Bulk_quote>("No-123", 30, 20, 0.2));
basket.push_back(make_shared<Bulk_quote>("No-123", 30, 20, 0.2));
basket.push_back(make_shared<Bulk_quote>("No-123", 30, 20, 0.2));*/
double total=0.0;
for (const auto&i : basket)
{
total += i.net_price(20);
}
cout << total << endl;
}
15.29
不一致,之前vector的模板是Quote的对象,不是指针和引用,所以静态类型与动态类型一致,存入的Bulk_quote对象自动被转化成Quote对象,所以调用是Quote::net_price。
int main()
{
/* vector<Quote> basket;
basket.push_back(Bulk_quote("No-123", 30, 20, 0.2));
basket.push_back(Bulk_quote("No-123", 30, 20, 0.2));
basket.push_back(Bulk_quote("No-123", 30, 20, 0.2));*/
vector<shared_ptr<Quote>>basket;
basket.push_back(make_shared<Bulk_quote>("No-123", 30, 20, 0.2));
basket.push_back(make_shared<Bulk_quote>("No-123", 30, 20, 0.2));
basket.push_back(make_shared<Bulk_quote>("No-123", 30, 20, 0.2));
double total=0.0;
for (const auto&i : basket)
{
total += i->net_price(20);
}
cout << total << endl;
}
15.30
class Basket
{
public:
void add_item(const Quote& sales)
{
items.insert(shared_ptr<Quote>(sales.clone()));
}
double total_receipt(ostream& os);
private:
static bool compare(const shared_ptr<Quote>& lhs, const shared_ptr<Quote>& rhs)
{
return lhs->isbn() < rhs->isbn();
}
multiset<shared_ptr<Quote>, decltype(compare)*>items{ compare };
};
double Basket::total_receipt(ostream& os)
{
double sum = 0.0;
for (auto beg = items.cbegin(); beg != items.cend(); beg = items.upper_bound(*beg))
{
sum += print_total(os, **beg, items.count(*beg));
}
os << "The total sale:" << sum << endl;
return sum;
}
15.31
Query(s1) | Query(s2) & ~ Query(s3);
// WordQuery, NotQuery, AndQuery, OrQuery, Query
Query(s1) | (Query(s2) & ~ Query(s3));
// WordQuery, NotQuery, AndQuery, OrQuery, Query
(Query(s1) | (Query(s2)) | (Query(s3) & Query(s4));
// WordQuery, AndQuery, OrQuery, Query
15.32
– 拷贝:当Query对象被拷贝时,会调用合成的拷贝构造函数,拷贝Query的数据成员,成员q由于是shared_ptr,其引用计数会加1.
– 移动:当Query对象被移动时,会调用合成移动构造函数,会移动数据成员到新的对象。在这个例子中,新对象中的shared_ptr会指向原对象的shared_ptr所指向的地址,新对象的shared_ptr的引用计数加1,原对象的shared_ptr的引用计数减1。
– 赋值:调用合成的赋值函数,结果与拷贝操作相同。
– 销毁:调用合成的析构函数,shared_ptr的引用计数减1,如果其引用计数减至0,则会释放它指向的内存
15.33
由于Query_base类中没有需要分配内存的数据成员,所以发生拷贝、移动、赋值或销毁,合成的版本就可以用,Query_base是抽象类,所以发生拷贝、移动、赋值或销毁时,操作的其实是它对应类型的子类。
15.34
略;
15.35
**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 string rep()const = 0;
};
class Query:public Query_base
{
friend Query operator~(const Query&);
friend Query operator&(const Query&, const Query&);
friend Query operator|(const Query&, const Query&);
public:
Query(string&s):q(new WordQuery(s)) {}
QueryResult eval(const TextQuery& t)const { return q->eval(t); }
string rep()const override{ return q->rep(); }
private:
Query(shared_ptr < Query_base>query):q(query) {};
shared_ptr<Query_base>q;
};
class WordQuery:public Query_base
{
friend class Query;
WordQuery(const string& s):query_word(s) {}
QueryResult eval(const TextQuery& t)const
{
return t.query(query_word);
}
string rep()const override{ return query_word; }
string query_word;
};
class NotQuery :public Query_base
{
friend Query operator~(const Query&);
NotQuery(const Query& q):query(q) {}
string rep()const { return "~(" + query.rep() + ")"; }
QueryResult eval(const TextQuery&)const;
Query query;
};
inline Query operator~(const Query& operand)
{
return shared_ptr<Query_base>(new NotQuery(operand));
}
class BinaryQuery:public Query_base
{
protected:
BinaryQuery(const Query& l, const Query& r, string s) :lhs(l),rhs(r),opSym(s){}
string rep()const
{
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, "&") {}
QueryResult eval(const TextQuery&) const {}
};
inline Query operator& (const Query& lhs, const Query& rhs)
{
return std::shared_ptr<Query_base>(new AndQuery(lhs, rhs));
}
class OrQuery : public BinaryQuery
{
friend Query operator| (const Query&, const Query&);
OrQuery(const Query& left, const Query& right) : BinaryQuery(left, right, "|") {}
QueryResult eval(const TextQuery&) const {}
};
inline Query operator| (const Query& lhs, const Query& rhs)
{
return std::shared_ptr<Query_base>(new OrQuery(lhs, rhs));
}**