C++ Primer 中文版(第 5 版)练习解答合集
自己写的解答,如有错误之处,烦请在评论区指正!
1
对于某些成员,基类希望它的派生类各自定义适合自身的版本(而不是不作改变直接继承),此时基类就将这些成员定义成虚成员。
2
对于 protected,派生类有权访问,但是其他用户无法访问
对于 private,派生类和其他用户都无法访问
3
#pragma once
#include <iostream>
using std::ostream;
using std::endl;
#include <string>
using std::string;
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;
};
class Bulk_quote : public Quote {
public:
double net_price(size_t) const override;
};
double printTotal(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;
}
4
a. 不正确,一个类不能派生自身
b. 正确
c. 不正确,在声明中不能写派生列表,这些细节都要放在类的主体中。
5
#pragma once
#include <iostream>
using std::ostream;
using std::endl;
#include <string>
using std::string;
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;
};
class Bulk_quote : public Quote {
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) { }
double net_price(size_t) const override;
private:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const {
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
double printTotal(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;
}
6
Quote.h 代码见上一题。
CppPrimerCh15.cpp:
#include <iostream>
#include "Quote.h"
using namespace std;
int main()
{
Quote q("Harry Potter", 15.0);
Bulk_quote bq("C++ Primer", 65.0, 3, 0.5);
printTotal(cout, q, 5);
printTotal(cout, bq, 5);
return 0;
}
运行结果:
ISBN: Harry Potter # sold: 5 total due: 75
ISBN: C++ Primer # sold: 5 total due: 162.5
7
class Limited_bulk_quote : public Quote {
public:
Limited_bulk_quote() = default;
Limited_bulk_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), max_qty(qty), discount(disc) { }
double net_price(size_t) const override;
private:
size_t max_qty = 0;
double discount = 0.0;
};
double Limited_bulk_quote::net_price(size_t cnt) const {
if (cnt <= max_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
8
静态类型在编译时总是已知的,它是变量声明时的类型或表达式生成的类型
而动态类型则是变量或表达式表示的内存中的对象的类型,动态类型直到运行时才可知
9
#include <iostream>
#include "Quote.h"
using namespace std;
int main()
{
Bulk_quote derived;
Limited_bulk_quote anotherDerived;
Quote* baseP = &derived;
Quote& baseR = derived;
Quote& baseR1 = anotherDerived;
return 0;
}
10
read 函数的第一个参数接受一个 iostream 的引用,实际用了 ifstream 类型来调用,这是派生类向基类的隐式转换(仅对指针或引用类型有效)。因为派生类中包括了基类的成员,所以 iostream 的引用可以指向派生类中基类的这部分。
11
#pragma once
#include <iostream>
using std::ostream;
using std::cout;
using std::endl;
#include <string>
using std::string;
/*=============================================
Quote
=============================================*/
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;
virtual void debug() const {
cout << "bookNo: " << bookNo
<< "\nprice: " << price << endl;
}
private:
string bookNo;
protected:
double price = 0.0;
};
double printTotal(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;
}
/*=============================================
Bulk_quote
=============================================*/
class Bulk_quote : public Quote {
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Quote(book, p), min_qty(qty), discount(disc) { }
double net_price(size_t) const override;
virtual void debug() const override {
cout << "min_qty: " << min_qty
<< "\ndiscount: " << discount << endl;
}
private:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const {
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
CppPrimerCh15.cpp:
#include <iostream>
#include "Quote.h"
using namespace std;
int main() {
Quote base("Harry Potter", 15.0);
Bulk_quote derived("C++ Primer", 65.0, 3, 0.5);
base.debug();
putchar('\n');
derived.debug();
putchar('\n');
derived.Quote::debug();
return 0;
}
运行结果:
bookNo: Harry Potter
price: 15
min_qty: 3
discount: 0.5
bookNo: C++ Primer
price: 65
12
有必要的时候可以这么做,这两个关键字不冲突:
- override 关键字声明了该函数是对基类中某个虚函数的重写,有助于编译器发现错误,如果参数列表和基类中虚函数不一致,编译器就会报错,防止编译器误认为这是个重载
- final 关键字则声明了该函数不允许再被派生类重写
13
有问题,derived 类中的 print 函数体中又调用了 print 函数,本意应该是调用基类中的 print,但是如果不显示地指出作用域,就会调用自身引发无限递归。另一个问题是最好在派生类重写的函数头后面加上 override 防止参数列表写错。
#include <iostream>
#include "Quote.h"
using namespace std;
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) override {
Base::print(os);
os << " " << i;
}
private:
int i;
};
int main() {
Derived d;
d.print(cout);
return 0;
}
14
a. base::print()
b. derived::print()
c. base::name()
d. base::name()
e. base::print()
f. derived::print()
15
#pragma once
#include <iostream>
using std::ostream;
using std::cout;
using std::endl;
#include <string>
using std::string;
/*=============================================
Quote
=============================================*/
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;
virtual void debug() const {
cout << "bookNo: " << bookNo
<< "\nprice: " << price << endl;
}
private:
string bookNo;
protected:
double price = 0.0;
};
double printTotal(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;
}
/*=============================================
Disc_quote
=============================================*/
class Disc_quote : public Quote {
public:
Disc_quote() = default;
Disc_quote(const string& book, double price, size_t qty, double disc) :
Quote(book, price), quantity(qty), discount(disc) { }
double net_price(size_t) const = 0;
protected:
size_t quantity = 0;
double discount = 0.0;
};
/*=============================================
Bulk_quote
=============================================*/
class Bulk_quote : public Disc_quote {
public:
Bulk_quote() = default;
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Disc_quote(book, p, qty, disc) { }
double net_price(size_t) const override;
virtual void debug() const override {
cout << "min_qty: " << min_qty
<< "\ndiscount: " << discount << endl;
}
private:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const {
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
/*=============================================
Limited_bulk_quote
=============================================*/
class Limited_bulk_quote : public Disc_quote {
public:
Limited_bulk_quote() = default;
Limited_bulk_quote(const string& book, double p, size_t qty, double disc) :
Disc_quote(book, p, qty, disc) { }
double net_price(size_t) const override;
private:
size_t max_qty = 0;
double discount = 0.0;
};
double Limited_bulk_quote::net_price(size_t cnt) const {
if (cnt <= max_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
16
见 15
17
不允许使用抽象类类型 “Disc_quote” 的对象
函数 double net_price(size_t) const
是纯虚拟函数
18
// 只有D公有地继承B时,用户代码才能使用D向B的转换
Base *p = &d1; // 正确
p = &d2; // 错误
p = &d3; // 错误
// 派生类的派生类也是同理
// 只有DD公有地继承D时,用户代码才能使用DD向D的转换,然后再进行D到B的转换
p = &dd1; // 正确
p = &dd2; // 错误
p = &dd3; // 错误
19
class Base {
public:
int pub_mem;
protected:
int prot_mem;
private:
int priv_mem;
};
class Pub_Derv : public Base {
void memfcn(Base& b) { b = *this; }
};
class Prot_Derv : protected Base {
void memfcn(Base& b) { b = *this; }
};
class Priv_Derv : private Base {
void memfcn(Base& b) { b = *this; }
};
class Derived_from_Public : public Pub_Derv {
void memfcn(Base& b) { b = *this; }
};
class Derived_from_Protected : public Prot_Derv {
void memfcn(Base& b) { b = *this; }
};
class Derived_from_Private : public Priv_Derv {
void memfcn(Base& b) { b = *this; } // 只有这个是非法的
};
对于 Pub_Derv、Prot_Derv、Priv_Derv 这三个类,不论 D 以什么方式继承 B,D 的成员函数和友元都能使用派生类向基类的转换,所以对这三个类来说成员函数都是合法的
对于 Derived_from_Public、Derived_from_Protected、Derived_from_Private 来说,如果 D 继承 B 的方式是 public 或者 protected,DD(D 的派生类)才能使用派生类向基类的转换,所以只有 Derived_from_Private 的成员函数非法
20
略
21
图形
2D 图形:图形
3D 图形:图形
矩形:2D 图形
圆形:2D 图形
……
长方体:3D 图形
球体:3D 图形
……
22
略
23
#include <iostream>
#include "Quote.h"
using namespace std;
class Base {
public:
virtual int fcn() {
cout << "Base::fcn()\n";
return 0;
}
};
class D1 : public Base {
public:
virtual int fcn() override {
cout << "D1::fcn()\n";
return 0;
}
};
class D2 : public D1 {
public:
virtual int fcn() override {
cout << "D2::fcn()\n";
return 0;
}
};
int main() {
Base bobj;
D1 d1obj;
D2 d2obj;
Base *bp1 = &bobj, *bp2 = &d1obj, *bp3 = &d2obj;
bp1->fcn();
bp2->fcn();
bp3->fcn();
return 0;
}
运行结果:
Base::fcn()
D1::fcn()
D2::fcn()
24
基类通常需要定义一个虚析构函数。虚析构函数可以确保编译器执行正确的析构函数版本(例如一个指针的静态类型与指向对象的动态类型不一致的时候)。
25
因为可以预见 Disc_quote 会有自己的派生类,如果去掉默认构造函数的话,因为我们为 Disc_quote 定义了一个接受 4 个参数的自定义构造函数,于是默认构造函数是删除的,Disc_quote 的派生类如 Bulk_quote 的默认构造函数也将会是删除的,因为 Bulk_quote 的 Disc_quote 基类部分无法被构造。
26
Quote.h:
#pragma once
#include <iostream>
using std::ostream;
using std::cout;
using std::endl;
#include <string>
using std::string;
#include <utility>
using std::pair;
/*=============================================
Quote
=============================================*/
class Quote {
public:
//Quote() = default;
Quote() : bookNo(""), price(0.0) {
printf("Quote constructor\n");
}
//Quote(const Quote& rhs) = default;
Quote(const Quote& rhs) : bookNo(rhs.bookNo), price(rhs.price) {
printf("Quote copy constructor\n");
}
//Quote(Quote&& rhs) = default;
Quote(Quote&& rhs) noexcept :
bookNo(std::move(rhs.bookNo)), price(std::move(rhs.price)) {
printf("Quote move constructor\n");
}
//Quote& operator=(const Quote& rhs) = default;
Quote& operator=(const Quote& rhs) {
printf("Quote copy assignment\n");
bookNo = rhs.bookNo;
price = rhs.price;
return *this;
}
//Quote& operator=(Quote&& rhs) = default;
Quote& operator=(Quote&& rhs) noexcept {
printf("Quote move assignment\n");
bookNo = rhs.bookNo;
price = rhs.price;
return *this;
}
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;
virtual ~Quote() {
printf("Quote deconstructor\n");
}
virtual void debug() const {
cout << "bookNo: " << bookNo
<< "\nprice: " << price << endl;
}
private:
string bookNo;
protected:
double price = 0.0;
};
double printTotal(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;
}
/*=============================================
Disc_quote
=============================================*/
class Disc_quote : public Quote {
public:
Disc_quote() = default;
Disc_quote(const string& book, double price, size_t qty, double disc) :
Quote(book, price), quantity(qty), discount(disc) { }
//virtual ~Disc_quote() = default;
virtual ~Disc_quote() {
printf("Disc_quote deconstructor\n");
}
virtual double net_price(size_t) const = 0;
pair<size_t, double> discount_policy() const {
return { quantity, discount };
}
protected:
size_t quantity = 0;
double discount = 0.0;
};
/*=============================================
Bulk_quote
=============================================*/
class Bulk_quote : public Disc_quote {
public:
//Bulk_quote() = default;
Bulk_quote() : Disc_quote(), min_qty(0), discount(0.0) {
printf("Bulk_quote constructor\n");
}
//Bulk_quote(const Bulk_quote& rhs) = default;
Bulk_quote(const Bulk_quote& rhs) :
Disc_quote(rhs), min_qty(rhs.min_qty), discount(rhs.discount) {
printf("Bulk_quote copy constructor\n");
}
//Bulk_quote(Bulk_quote&& rhs) = default;
Bulk_quote(Bulk_quote&& rhs) noexcept :
Disc_quote(std::move(rhs)),
min_qty(std::move(rhs.min_qty)), discount(std::move(rhs.discount)) {
printf("Bulk_quote move constructor\n");
}
//Bulk_quote& operator=(const Bulk_quote& rhs) = default;
Bulk_quote& operator=(const Bulk_quote& rhs) {
printf("Bulk_quote copy assignment\n");
Disc_quote::operator=(rhs);
min_qty = rhs.min_qty;
discount = rhs.discount;
return *this;
}
//Bulk_quote& operator=(Bulk_quote&& rhs) = default;
Bulk_quote& operator=(Bulk_quote&& rhs) noexcept {
printf("Bulk_quote move assignment\n");
Disc_quote::operator=(std::move(rhs));
min_qty = rhs.min_qty;
discount = rhs.discount;
return *this;
}
//virtual ~Bulk_quote() = default;
virtual ~Bulk_quote() {
printf("Bulk_quote deconstructor\n");
}
Bulk_quote(const string& book, double p, size_t qty, double disc) :
Disc_quote(book, p, qty, disc) { }
double net_price(size_t) const override;
virtual void debug() const override {
cout << "min_qty: " << min_qty
<< "\ndiscount: " << discount << endl;
}
private:
size_t min_qty = 0;
double discount = 0.0;
};
double Bulk_quote::net_price(size_t cnt) const {
if (cnt >= min_qty)
return cnt * (1 - discount) * price;
else
return cnt * price;
}
CppPrimerCh15.cpp:
#include <iostream>
#include "Quote.h"
using namespace std;
int main() {
Quote b1;
Quote b2(b1);
Quote b3(std::move(b1));
b1 = b2;
b1 = std::move(b2);
Bulk_quote d1;
Bulk_quote d2(d1);
Bulk_quote d3(std::move(d1));
d1 = d2;
d1 = std::move(d2);
return 0;
}
运行结果:
Quote move constructor
Quote copy assignment
Quote move assignment
Quote constructor
Bulk_quote constructor
Quote copy constructor
Bulk_quote copy constructor
Quote copy constructor
Bulk_quote move constructor
Bulk_quote copy assignment
Quote copy assignment
Bulk_quote move assignment
Quote copy assignment
Bulk_quote deconstructor
Disc_quote deconstructor
Quote deconstructor
Bulk_quote deconstructor
Disc_quote deconstructor
Quote deconstructor
Bulk_quote deconstructor
Disc_quote deconstructor
Quote deconstructor
Quote deconstructor
Quote deconstructor
Quote deconstructor
27
在类中加入
using Disc_quote::Disc_quote;