C++ Primer Plus v6 Chapter 13 exercise 3

write in advance

主要考察ABC(abstract basic type) 的应用。

at least a  pure virutal function existing in the class makes a class abstract base class.

abstract basic class is a special kind of class, it provides the common charactericstrcs that its derived classes would need.  a ABC cannot be instantiated, which means that u cannot have a ABC object. However, having a reference whose type is ABC is valid, which is a similar situation when it comes to pointers. that is, i can have a code fragment like this.

// here ,basebase is a ABC, and baseDMA derived from it
basebase* ptr;
baseDMA obj1;

basebase& rfb = obj1;
ptr = &obj1; 

the reason why code like this  works is that upcasting is  working here.

upcasting is a mechanism that a base class reference or pointer can refer to or point to derived class object without explicit type cast. it is used commonly in the condition that there is a inheritance. you can see them in my method file, test file and many other places.

  注意 ABC 无法实例化为对象。我们可以不为它提供函数的定义,如果你和我一样坚持提供,也是允许的。

此外,此处我设置了两级继承关系。 baseDMA 继承自 basebase,lacksDMA 与 hasDMA继承自 baseDMA        

head file

#ifndef DMA_H_
#define DMA_H_
#include<iostream>

class basebase
{
private:
	int rating;
public:
	basebase(int r = 0);
	basebase(const basebase&);
	basebase& operator=(const basebase&);
	virtual ~basebase() = 0;
	friend std::ostream& operator<<(std::ostream&, const basebase&);
	virtual void show() const;
};

class baseDMA : public basebase
{
private:
	char* label;
public:
	baseDMA(const char*label = "NULL",int r = 0);
	baseDMA(const baseDMA&);
	virtual ~baseDMA();
	baseDMA& operator=(const baseDMA&);
	friend std::ostream& operator<<(std::ostream& os,const baseDMA& rs);
	virtual void show() const;
};

class lacksDMA : public baseDMA 
{
private:
	enum {COL_LEN = 40};
	char color[COL_LEN];
public:
	lacksDMA(const char* c = "black", const char* label = "NULL", int r = 0);
	lacksDMA(const char* c, const baseDMA& );
	friend std::ostream& operator<<(std::ostream&, const lacksDMA&);
	void show() const;
};

class hasDMA :public baseDMA
{
private:
	char* style;
public:
	hasDMA(const char* s = "none", const char* label = "null", int r = 0);
	hasDMA(const char*s, const baseDMA&);
	hasDMA(const hasDMA&);
	~hasDMA();
	hasDMA& operator=(const hasDMA& rhs);
	friend std::ostream& operator<<(std::ostream&, const hasDMA& rhs);
	void show() const;
};
#endif

method file

#include"DMA.H"

basebase::basebase(int r)
{
	rating = r;
}

basebase::~basebase()
{	}

basebase::basebase(const basebase& rhs)
{
	rating = rhs.rating;
}

basebase& basebase::operator=(const basebase& rhs)
{
	if (  &rhs == this)
		return *this;
	rating = rhs.rating;
	return *this;
}

std::ostream& operator<<(std::ostream& os, const basebase& rhs)
{
	os << rhs.rating << " ";
	return os;
}

baseDMA::baseDMA(const char* lab, int r)
	: basebase(r)
{
	label = new char[strlen(lab) + 1];
	strcpy(label,lab);
}

baseDMA::baseDMA(const baseDMA& rhs)
	: basebase(rhs)
{
	label = new char[strlen(rhs.label) + 1];
	strcpy(label,rhs.label);
}

baseDMA::~baseDMA()
{
	delete[] label;
}

baseDMA& baseDMA::operator=(const baseDMA& rhs)
{
	if (&rhs == this)
		return *this;
	basebase::operator=(rhs);	// invoke the base class
	delete[] label;
	label = new char[strlen(rhs.label) + 1];
	strcpy(label,rhs.label);
	return *this;
}

std::ostream& operator<<(std::ostream& os, const baseDMA& rhs)
{
	rhs.basebase::show();	// attention
	os << rhs.label << " ";
	return os;
}

lacksDMA::lacksDMA(const char* colo, const char* label, int r)
	:baseDMA(label,r)
{
	strncpy(color,colo,COL_LEN);
	color[COL_LEN - 1] = '\0';
}

lacksDMA::lacksDMA(const char* colo, const baseDMA& rhs)
	: baseDMA(rhs)
{
	strncpy(color, colo, COL_LEN);
	color[COL_LEN - 1] = '\0';
}

std::ostream& operator<<(std::ostream& os, const lacksDMA& rhs)
{
	os << baseDMA(rhs) ;
	os << rhs.color << " ";
	return os;
}

hasDMA::hasDMA(const char* s, const char* label, int r)
	: baseDMA(label,r)
{
	style = new char[strlen(s) + 1];
	strcpy(style, s);
}

hasDMA::hasDMA(const char* s, const baseDMA& rhs)
	: baseDMA(rhs)
{
	style = new char[strlen(s) + 1];
	strcpy(style,s);
}

hasDMA::hasDMA(const hasDMA& rhs)
	: baseDMA(rhs)
{
	style = new char[strlen(rhs.style) + 1];
	strcpy(style,rhs.style);
}

hasDMA::~hasDMA()
{
	delete[] style;
}

hasDMA& hasDMA::operator=(const hasDMA& rhs)
{
	if (&rhs == this)
		return *this;
	baseDMA::operator=(rhs);
	delete[] style;
	style = new char[strlen(rhs.style) + 1];
	strcpy(style, rhs.style);
	return *this;
}

std::ostream& operator<<(std::ostream& os, const hasDMA& rhs)
{
	os << baseDMA(rhs);
	os << rhs.style << " ";
	return os;
}

void basebase::show() const
{
	std::cout << rating << " ";
	return;
}

void baseDMA::show() const
{
	basebase::show();
	std::cout << label << " ";
	return;
}

void lacksDMA::show() const
{
	baseDMA::show();
	std::cout << color << " ";
}

void hasDMA::show() const
{
	baseDMA::show();
	std::cout << style << " ";
}

test file

此处仿照书上的做法, 使用了一个数据类型全为base class 的数组,并在for循环中逐个初始化数组中的指针变量,此处数组中的变量类型为baseDMA,其实也可以为basebase(ABC类)。只要指针的类型为基类即可。ABC只是限制了不能实例化该类型的对象,并没有限制该类型指针,引用的使用。

注意到我初始化数组中对应指针变量的内容时使用了new 加上对应的类型。你可能很熟悉built-in type类型变量使用 new 申请内存,并初始化,而user-defined type实际上也遵循同样的语法规则。

此处我有一个while循环,while循环的测试语句写得有点笨重,循环退出条件为 输入的数值为 1或2或3。我需要保证每个变量都被正确地初始化,否则在我第二个 for 循环处,输出未初始化的指针所指向的内容,它的行为是未定义的,undefined。我们应该避免这样的情况。

目前而言,类的定义和使用都是正确的。注意到我用了switch,多分支选择。

#include"DMA.h"
#include<iostream>
const int ArrSize = 4;

int main()
{
	basebase* haha[3];
	lacksDMA obj1;
	haha[0] = &obj1;
	basebase& rhs = obj1;

	baseDMA* ptr[ArrSize];
	const char* ptc[ArrSize] = {"C++","hello","world","bye"};
	const char* ptco[ArrSize] = {"red","yellow","green","purple"};
	const char* ptsty[ArrSize] {"guitar","car","clothes","book"};
	const int num[ArrSize] = { 2,4,5,6 };

	int select = -1;
	for (int i = 0; i < ArrSize; i++)
	{
		std::cin >> select;
		while (select != 1 && select != 2 && select != 3)
		{
			std::cout << "enter the right number\n";
			std::cin >> select;
		}
		//std::cout << "great, enter the right number\n";
		switch (select)
		{
		case 1: ptr[i] = new baseDMA(ptc[i],num[i]);
			break;
		case 2: ptr[i] = new lacksDMA(ptco[i],ptc[i],num[i]);
			break;
		case 3: ptr[i] = new hasDMA(ptsty[i],ptc[i],num[i]);
			break;
		}
		std::cout << "good job, next one\n";
	}

	for (int i = 0; i < ArrSize; i++)
	{
		std::cout << std::endl;
		(*(ptr+i))->show();  //ptr[i]->show();
	}
	return 0;
}

summary

代码复用很有必要。如此简单的类的示例,我写了很长时间,大部分时间都放在了没什么区别的重复部分。开始感受到继承的重要性了。

有一段时间没有写关于类的习题,在写友元函数发生了点小插曲。是友元函数和成员函数不一样,当我们提供友元函数的定义时,它不需要也不能有类解析符,因为友元是类的朋友,不是类的部分。

另一件丢脸的事情是,我增加了虚函数show(),用于展示对象的内容。在调试的过程中,发现无论我如何修改,输出都未发生变化。正当我惶恐奇怪之际,发现 test file中负责输出的函数并非是 show() 函数。——写练习时,至少得保持头脑清醒。至少应该知道自己改的部分,和测试的部分。

在test file中,我并没有手动逐个测试三个类的 copy constructor, assignment operator。一是暂时没想到很有意思的测试代码,又不想写很笨的代码,二是因为我反复核对这些函数的具体实现,相信自己已经正确定义。如果你看到这里,你有兴趣,你可以自己试试。

此外,test file中的 指针数组也值得注意。自己习惯了数值中的变量类型都是内置类型,当数组元素变量为指针时,出现了一会迷糊。

明天我把下一题也写了。然后推进到 multiple inheritance。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值