C++ Primer 中文第 5 版练习答案 第 15 章 面向对象程序设计(1~27)

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;
上一篇:PAT乙级刷题/1017 A除以B/C++实现


下一篇:PAT乙级真题 1032 挖掘机技术哪家强 (20 分) Java