《C++ Primer Plus》第13章编程练习 答案及思考

完整的思考与解答入口

  1. 以下面的类声明为基础:
class Cd{
private:
	char performers[50];
	char label[20];
	int selections;
	double playtime;
public:
	Cd(char * s1, char * s2, int n, double x);
	Cd(const Cd & d);
	Cd();
	~Cd();
	void Report() const;
	Cd & operator=(const Cd & d);
};

派生出一个Classic类,并添加一组char成员,用于存储指出CD中主要作品的字符串。修改上述声明,使基类的所有函数都是虚的。如果上述定义声明的某个方法并不需要,则请删除它。使用下面的程序测试您的产品:

int main()
{
    Cd c1("Beatles","Capitol",14,35,5);
    Classic c2 = Classic("Piano Sonata in B flat, Fantasia in C","Alfred Brendel", "Philips", 2, 57.17);
    Cd *pcd = &c1;

	cout << "Using object directly:\n";
    c1.Report();
    c2.Report();

    cout << "Using type cd * pointer to objects:\n";
    pcd->Report();
    pcd = &c2;
    pcd->Report();

    cout << "Calling a function with a Cd reference argument:\n";
    Bravo(c1);
    Bravo(c2);

    cout << "Testing assignment: ";
    Classic copy;
    copy = c2;
    copy.Report();

    return 0;
}

完善后我的答案:

#include <iostream>
#include <cstring>
using namespace std;

class Cd{
protected:
	char performers[50];
	char label[20];
	int selections;
	double playtime;
public:
	Cd(char* s1, char* s2, int n, double x);
	Cd();
	virtual void Report() const;
	virtual Cd& operator=(const Cd& d);
};

Cd::Cd(char * s1, char * s2, int n, double x): selections(n), playtime(x){
	strcpy(performers, s1);
	strcpy(label, s2);
}
Cd::Cd(){} //一定得写,很重要!不然会报错!
void Cd::Report()const {
    	cout << "performers: "<< performers << endl;
    	cout << "label: " << label << endl;
    	cout << "number of selections: " << selections << endl;
    	cout << "playing time in minutes: " << playtime << endl;
    	cout << endl;
}
Cd& Cd::operator=(const Cd& d){
	strcpy(performers, d.performers);
	strcpy(label, d.label);
	selections = d.selections;
	playtime = d.playtime;
	return *this;
}

class Classic : public Cd{
protected:
	char majorworkers[100];
public:
	Classic(char* s1, char* s2, char* s3, int n, double x);
	Classic();
	void Report() const;
	Classic& operator = (Classic& c);
};

Classic::Classic(char* s1, char* s2, char* s3, int n, double x): Cd(s2, s3, n, x){
	strcpy(majorworkers, s1);
}
Classic::Classic() {} //一定得写,很重要!不然会报错!

void Classic::Report()const{
	cout << "majorworkers: " << majorworkers << endl;
	cout << "performers: "<< performers << endl;
	cout << "label: " << label << endl;
	cout << "number of selections: " << selections << endl;
	cout << "playing time in minutes: " << playtime << endl;
	cout << endl;
}

Classic& Classic::operator = (Classic& c){
	strcpy(majorworkers, c.majorworkers);
	Cd::operator=(c);
	return *this;
}


void Bravo(const Cd & disk){
	disk.Report();
}
int main()
{
	Cd c1("Beatles", "Capitol", 14, 35.5);
   	Classic c2 = Classic("Piano Sonata in B flat, Fantasia in C", "Alfred Brendel","Philips", 2, 57.17);
	Cd *pcd = &c1;

	cout << "Using object directly:\n";
    	c1.Report();
    	c2.Report();

    	cout << "Using type cd * pointer to objects:\n";
    	pcd->Report();
    	pcd = &c2;
    	pcd->Report();

    	cout << "Calling a function with a Cd reference argument:\n";
    	Bravo(c1);
    	Bravo(c2);

    	cout << "Testing assignment: ";
    	Classic copy;
    	copy = c2;
    	copy.Report();

    	return 0;
}

注意点:

  • 重载赋值号的格式:point& operate = (const point& p) {x=p.x;y=p.y;return *this;}
  • 如果我在基类定义了带参的构造函数,而主函数中用到了派生类的无参构造函数(就是指定义一个派生类的对象,未赋初值),那么该派生类和基类的无参构造函数都得写出来。为什么呢?
    • 派生类的无参构造函数为什么必须得写?我还不是很清楚,百度也没查到为什么,但就是不行。
    • 基类的无参构造函数必须得写,因为基类已经定义了带参的构造函数,当派生类的构造函数调用基类构造函数的时候就会调用显示定义的,如果说基类并未定义带参构造函数,派生类的构造函数将调用基类的默认构造函数。
  • 类的display函数一般定义成const函数
  • 将派生类对象的地址赋给基类指针时,如果基类的虚函数在该派生类被重新定义了,该指针调用该虚函数的时候调用的是派生类内的新定义的虚函数。
  • 如果用到继承,基类的成员尽量定义为protected,不然派生类没法访问
  1. 完成练习1,但让两个类使用动态内存分配而不是长度固定的数组来记录字符串。
  • 动态分配内存就涉及到new的使用,析构函数就要自己定义
  • 这道题主要修改了类的构造函数、析构函数、还有重载运算符部分涉及到指针的部分。

注意点:

  • strlen和sizeof的区别?在计算字符串长度的时候是否两个都能用?
  • new一个字符型指针指向字符型数组的时候,长度如何计算?
  • 接上一点,在带参的构造函数中,通过参数传递了字符数组,new了之后,如何将字符数组赋给字符指针,直接相等行不行?

贴一个带指针的重载赋值运算符改进的标准答案:

Cd & Cd::operator=(const Cd & d)
{
    if (this == &d)
        return *this;
    delete [] label;
    delete [] performers;
    performers = new char[strlen(d.performers) + 1];
    strcpy(performers, d.performers); 
    label = new char[strlen(d.label) + 1];
    strcpy(label, d.label);    
    selections = d.selections;
    playtime = d.playtime;  
    return *this;
}
  1. Benevolent Order of Programmers用来维护瓶装葡萄酒箱。为描述它,BOP Portmaster 设置了一个Port类,其声明如下:
#include < iost ream>
using namespace std;
class Port{
private:
	char* brand;
	char sty1e[20]; 
	int bottles ;
public:
	Port (const char* br = "none", const char* st = "none", int b = 0) ;
	Port (const Port & p) ;
// copy const ructor
	virtual ~Port(){ delete [] br and;}
	Port & operator= (const Port & pl ;
	Port & operator+=(int b) ;
// adds b to bottles .
	Port & operator-=(int b) ;
// subtracts b from bottles, if a vailable
	int BottleCount() const{ return bottles;]
	virtual void Show() const;
	friend ostream & operator<< (ostream & os,const Port & p) ;
};

show( )方法按下面的格式显示信息:
Brand: Gal1o
Kind: tawny
Bottles: 20
operator<<( )函数按下面的格式显示信息(末尾没有换行符):
Gallo,tawny, 20
PorMaster完成了Port 类的方法定义后派生了VintagePort 类,然后被解职—— 因为不小心将一瓶 45度Cockburn泼到了正在准备烤肉调料的人身上,VintagePort 类如下所示:

class VintagePort : public Port // style necessarily = "vintage"
private: .
	char* nickname ;
// i.e.. "The Noble" or "0ld Velvet", etc.
	int year;
// vintage year
public:
	VintagePort() ;
	VintagePort (const char* br, int b, const char* nn,int y);
	Vint agePort (const VintagePort & vp) ;
	~VintagePort{){ delete [] nickname ;}
	VintagePort & operator= lconst VintagePort & vp) ; 
	void Show() const ; 
	friend ostream & operator<< {ostream & os,const VintagePort & vp] ;
};

您被指定负责完成VintagePort.
a.第一个任务是重新创建Port方法定义,因为前任被开除时销毁了方法定义。
b.第二个任务是解释为什么有的方法重新定义了,而有些没有重新定义。
c.第三个任务是解释为何没有将operator=( )和opcrator<<( )声明为虚的。
d.第四个任务是提供VintagePort中各个方法的定义。

这是我完善后的答案:

b.有的方法是内联函数,不必重新定义。

c.基类的重载函数和友元函数不能被继承,所以不用声明为虚的。

#include <iostream>
#include <cstring>
using namespace std;

class Port{
private:
	char* brand;
	char style[20];
	int bottles ;
public:
	Port (const char* br = "none", const char* st = "none", int b = 0) ;
	Port (const Port & p);
	virtual ~Port(){ delete [] brand;}
	Port& operator = (const Port& pl);
	Port& operator += (int b);
	Port& operator -= (int b); 
	int BottleCount() const{ return bottles; }
	virtual void Show() const;
	friend ostream & operator << (ostream& os, const Port& p) ;
};

Port::Port (const char* br , const char* st , int b): bottles(b){
	brand = new char [strlen(br)+1]; //注意要加1!因为字符串在结尾处有一个'\0'用于标记末尾
	strcpy (brand, br); //这里不能写brand=br
	strcpy (style, st);
}
Port::Port(const Port& p){
	brand = new char [strlen (p.brand)+1];//注意sizeof和strlen的区别!!
	strcpy (brand, p.brand);
	strcpy (style, p.style);
	bottles = p.bottles;
}

Port& Port::operator = (const Port& p1){
	if(&p1==this) //疑惑:为什么要写成&p1而不是直接p1
		return *this;
	delete []brand; //把原来的指针delete掉!!
	brand = new char [strlen (p1.brand)+1];
	strcpy (brand, p1.brand);
	strcpy (style, p1.style);
	bottles = p1.bottles;
	return *this;
}

Port& Port::operator += (int b){
	bottles +=b;
	return *this;
}

Port& Port::operator -= (int b){
	bottles -=b;
	return *this;
}

void Port::Show()const{
	cout << "Brand:" << brand << endl;
	cout << "Kind:" << style << endl;
	cout << "Bottles:" << bottles << endl;
}

ostream& operator << (ostream& os, const Port& p){
	os << p.brand << ", " << p.style << ", " << p.bottles ;
	return os;
}

class VintagePort : public Port {
private:
	char* nickname;
	int year;
public:
	VintagePort() ; //这个一定要记得写
	VintagePort (const char* br, int b, const char* nn, int y);
	VintagePort (const VintagePort& vp) ;
	~VintagePort(){ delete [] nickname; }
	VintagePort & operator= (const VintagePort & vp) ;
	void Show() const ;
	friend ostream & operator<< (ostream & os, const VintagePort & vp) ;
};

VintagePort::VintagePort():  year(0) {
	Port("none","vintage",0);
	nickname = new char;
	nickname = '\0'; //字符串结束标志
}
VintagePort::VintagePort(const char* br, int b, const char* nn, int y): year(y), Port(br, "vintage", b){
	nickname = new char [strlen(nn)+ 1];
	strcpy (nickname, nn);
}

VintagePort::VintagePort(const VintagePort& vp): Port(vp) //这个太难了,竟然只能写在初始化列表里
{
	nickname = new char [strlen(vp.nickname)+ 1];
	strcpy (nickname, vp.nickname);
	year = vp.year;

}

VintagePort& VintagePort::operator = (const VintagePort &vp){
	if(&vp==this)
		return *this;
	delete []nickname;
	nickname = new char [strlen(vp.nickname)+ 1];
	strcpy (nickname, vp.nickname);
	year = vp.year;
	Port::operator =(vp);//难想到!
	return *this;
}

void VintagePort::Show() const {
	Port::Show();
	cout << "Nickname::" << nickname << endl;
	cout << "Year:" << year << endl;
}

ostream& operator << (ostream& os, const VintagePort& vp){
	os << (const Port&) vp;  //有点难!没想到要用强制类型转换,不知道用指针行不行?
	os << vp.nickname << "," << vp.year ;
}


int main()
{
    Port port1("gallo", "tawny", 20);
    cout << port1 << endl << endl;
    VintagePort vp("gallo", 24, "Old Velvet", 16);
    VintagePort vp2(vp);
    cout << vp2 << endl << endl;
    VintagePort vp3;
    vp3 = vp;
    cout << vp3 << endl << endl;

    Port * p_port;
    p_port = &port1;
    p_port->Show();
    cout << endl;
    p_port = &vp;
    p_port->Show();
    cout << endl;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值