本章所有编程练习的工程源码可在此处下载(点击此链接下载),供大家参考交流!
1. 以下面的类声明为基础:
{……代码省略……}
派生出一个Classic类,并添加一组char成员,用于存储指出CD中主要作品的字符串。修改上述声明,使基类的所有函数都是虚的。如果上述定义声明的某个方法并不需要,则请删除它。使用下面的程序测试您的产品:
{……代码省略……}
本题提供了基类,但是该基类并不能用于继承,因为函数不是虚函数。那么本题要求将基类修改为虚函数版本,然后派生出Classic的派生类,再给出实现文件,最后使用题中给出的测试程序进行测试。
首先,对于将题中基类修改为虚函数版本,我们只需要将基类的公有函数部分除构造函数和友元之外的函数,声明时前面加上关键字virtual即可;然后对于派生类,我们在私有成员里面添加一个char数组,然后将继承的函数写出来即可。
所以类声明的头文件cd.h如下所示:
// cd.h -- head file
#ifndef CD_H_
#define CD_H_
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();
virtual ~Cd();
virtual void Report() const;
virtual Cd & operator=(const Cd & d);
};
class Classic : public Cd
{
private:
char mainworks[50];
public:
Classic(char * s0, char * s1, char * s2, int n, double x);
Classic(char * s0, Cd & rs);
Classic();
virtual ~Classic();
virtual void Report() const;
virtual Classic & operator=(const Classic & d);
};
#endif
这里我将派生类的函数也设为了虚函数,其实没有必要,但是如果以后这个Classic类还需要继续往下派生,虚函数就能派上用场了。
接下来对于实现文件,首先需要注意的是构造函数的赋值,对于这种char数组的赋值,我们要么得使用for循环来一个一个地赋值,要么就要借助strcpy函数来赋值,而该函数在VS2017上必须使用安全版本才会不被警告或报错,所以我这里都是使用的strcpy_s函数来进行char数组的赋值。(关于strcpy函数和其安全版本strcpy_s函数的使用请点击链接)
其次,到了写派生类的函数定义时,我们要灵活使用成员初始化列表,将参数里的基类信息传递给基类的构造函数,而对于基类里不包含的成员变量,则必须在函数体内进行初始化。对于继承的公有函数,我们可以在派生类的函数体内部使用作用域解析运算符来调用基类函数,然后添加上新的派生类特有的内容。
实现文件cd.cpp如下所示:
// cd.cpp -- functions' definition
#include "stdafx.h"
#include <iostream>
#include <cstring>
#include "cd.h"
using namespace std;
Cd::Cd(char * s1, char * s2, int n, double x)
{
strcpy_s(performers, 50, s1);
strcpy_s(label, 20, s2);
selections = n;
playtime = x;
}
Cd::Cd(const Cd & d)
{
strcpy_s(performers, 50, d.performers);
strcpy_s(label, 20, d.label);
selections = d.selections;
playtime = d.playtime;
}
Cd::Cd()
{
strcpy_s(performers, 50, "null");
strcpy_s(label, 20, "null");
selections = 0;
playtime = 0;
}
Cd::~Cd()
{
}
void Cd::Report() const
{
cout << "In this CD disk, the performers are " << performers << endl;
cout << "The labels are " << label << endl;
cout << "There are " << selections << " selections and lasts " << playtime << " minutes.\n";
}
Cd & Cd::operator=(const Cd & d)
{
strcpy_s(performers, 50, d.performers);
strcpy_s(label, 20, d.label);
selections = d.selections;
playtime = d.playtime;
return *this;
}
Classic::Classic(char * s0, char * s1, char * s2, int n, double x) : Cd(s1, s2, n, x)
{
strcpy_s(mainworks, 50, s0);
}
Classic::Classic(char * s0, Cd & rs) : Cd(rs)
{
strcpy_s(mainworks, 50, s0);
}
Classic::Classic() : Cd()
{
strcpy_s(mainworks, 50, "null");
}
Classic::~Classic()
{
}
void Classic::Report() const
{
Cd::Report();
cout << "The main works of this disk are " << mainworks << endl;
}
Classic & Classic::operator=(const Classic & d)
{
if (this == &d)
return *this;
Cd::operator=(d);
strcpy_s(mainworks, 50, d.mainworks);
return *this;
}
接下来测试文件题中是直接给出的,我们对着敲上去就可以了。我这里由于IDE和习惯问题进行了微调。
测试文件cddisk.cpp如下所示:
// cddisk.cpp -- check the functions
#include "stdafx.h"
#include <iostream>
#include "cd.h"
using namespace std;
void Bravo(const Cd & disk);
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();
system("pause");
return 0;
}
void Bravo(const Cd & disk)
{
disk.Report();
}
程序运行结果如下图所示:
2.完成练习1,但让两个类使用动态内存分配而不是长度固定的数组来记录字符串。
练习1中,我们的成员变量的类型是使用的char数组,本题要求我们改为动态内存分配,所以成员变量的类型我们可以选为char指针,那么这里最最需要注意的就是,这样改过之后,析构函数就必须要发挥作用了,析构函数内部就必须要把构造函数里面初始化时动态分