C++ Primer Plus v6 Chapter13 exercise 4

 write in advance

简单的两个类,一个基类一个派生类,提供一些member functions, 需要完成member functions定义的书写。

基类中有 char* 变量用 new allocate memory,也有char数组,以及一个int 类型的变量。意图很明显,将两种字符存储方式放置在一起,在处理copy constructor, assignment operator时可以进行比对,加深理解。

派生类中同样是 char* 变量,用new 申请内存,存放字符串。当然, 在派生类的 special member functions 处,也复习了 public derived 的方法。——我们这章学习的内容。 

此处不提供test file。一是题目没有要求,二是想用不那么笨的方法测试,可能后续会补充。其实在完成了类的定义后,我们就可以像对待built-in type一样对待我们的class,所以需要测试的是这几个函数。此处,至少在这些练习中,测试接口并没有那么重要。——当然,未经过测试的接口不使用或者不一定正确。

head file

照着书上的写下来。我在类中定义了一个 static const int 类型的变量,作为类内常量。

派生类的初始化函数中少了一项 const char* 类型的变量,我给它补上了。

#ifndef PORT_H_
#define PORT_H_
#include<iostream>
class Port
{
private:
	static const int ArrSize = 20;
	char* brand;
	char style[ArrSize];
	int bottles;
public:
	Port(const char* br = "none", const char* st = "none", int bo = 0);
	Port(const Port&);
	virtual ~Port();
	Port& operator=(const Port&);
	Port& operator+=(int b);
	Port& operator-=(int b);
	int BottleCount() const { return bottles; }
	virtual void show() const;
	friend std::ostream& operator<<(std::ostream& os,const Port& rhs);
};

class VintagePort : public Port
{
private:
	char* nickname;
	int year;
public:
	VintagePort();
	VintagePort(const char* br, const char* sty, int b, const char* nm, int y);
	VintagePort(const VintagePort&);
	~VintagePort();
	VintagePort& operator=(const VintagePort&);
	void show() const;
	friend std::ostream& operator<<(std::ostream& os, const VintagePort&);
};
#endif

method file

基类的定义没什么好说的,都是很基本的操作。

派生类的copy constructor 调用基类的 copy constructor完成基类部分的初始化,派生类的operator=在确定不是自我赋值后采用同样的方法。虽然两者所做的事情很类似,但 copy constructor 用于初始化, operator= 用于赋值,且 copy constructor 可以使用memberlist initialization,因为它是 constructor。

#include"Port.h"

Port::Port(const char* br, const char* sty, int bot)
	: bottles(bot)
{
	brand = new char [strlen(br) + 1];
	strcpy(brand,br);
	strncpy(style,sty,ArrSize);
	style[ArrSize - 1] = '\0';
}

Port::Port(const Port& rhs)
	: bottles(rhs.bottles)
{
	delete[] brand;
	brand = new char[strlen(rhs.brand) + 1];
	strcpy(brand,rhs.brand);
	strncpy(style,rhs.style, ArrSize);
	style[ArrSize - 1] = '\0';
}

Port::~Port()
{
	delete[] brand;
}

Port& Port::operator=(const Port& rhs)
{
	if (&rhs == this)
		return *this;
	delete[] brand;
	brand = new char[strlen(rhs.brand) + 1];
	strcpy(brand,rhs.brand);
	strcpy(style, rhs.style);	// assume that rhs.style is 
	bottles = rhs.bottles;		// properly set, which is guaranteed
	return *this;		// by copy constructor, default constructor
}

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

Port& Port::operator-=(int b)
{
	if (bottles > b)
		bottles -= b;
	else
		std::cout << "bottoles no way to be negative\n";
	return *this;
}

void Port::show() const
{
	std::cout << "Brand: " << brand ;
	std::cout << "\nKind: " << style;
	std::cout << "\nBottles: " << bottles;
	return;
}

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

VintagePort::VintagePort()
	:Port(),year(2024)
{
	nickname = new char[strlen("C++") + 1];
	strcpy(nickname,"C++");
}

VintagePort::VintagePort(const char* br, const char* sty,
	int b, const char* nm, int y)
	: Port(br,sty,b), year(y)
{
	nickname = new char[strlen(nm) + 1];
	strcpy(nickname,nm);
}

VintagePort::VintagePort(const VintagePort& rhs)
	: Port(rhs),year(rhs.year)
{
	nickname = new char[strlen(rhs.nickname) + 1];
	strcpy(nickname,rhs.nickname);
}

VintagePort::~VintagePort()
{
	delete[] nickname;
}

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

void VintagePort::show() const
{
	Port::show();
	std::cout << "Nikename: " << nickname;
	std::cout << "\nyear: " << year;
	return;
}

std::ostream& operator<<(std::ostream& os, const VintagePort& rhs)
{
	os << (const Port&)rhs;
	os << rhs.nickname << " " << rhs.year << " ";
	return os;
}

这里有一个亮点,在 operator=() 函数中,我使用了 strcpy(), 而不是更安全的strncpy(),因为我的copy constructor,constructor 保证了该类型的变量都被正确地初始化,所以在赋值情况下,等式两边的数组都应该被正确地设置。

最后的 operator<< 函数中,使用了显示转换类型,将 派生类的rhs转换为基类,完成基类内容的输出。在对基类完成操作后,再输出派生类的内容。 

questions related to the exercise

一些方法重定义而一些没有:重定义的方法因为类的派生需要对派生类的数据进行操作,自然与基类的方法不同,自然需要声明为 virtual ,重定义。但类中可能有一些基类和派生类都需要的,不变的部分,比如此处的 BottoleCount(),在派生类中我们并不改变 bottle 的数量,也没有访问权限,这个方法在基类、派生类中都能满足我们的需要,自然不需要重定义。

operator= 与 operator<< 不是虚函数: 虚函数是指函数的参数列表,函数名都相同的,在派生类和基类中实现不相同任务的方法。operator= , operator<< 的参数列表就不相同,它们是函数重载 function overloading, 不是虚函数。

summary

现在,你可以很容易地看到,在派生类的方法中,它往往只处理派生类自己的那一部分变量。对于基类的部分,我们总有办法处理。

如果是copy constrcutor 或者 constructor,我们可以直接将 派生类的引用 作为参数传递给基类的方法,完成基类的操作。member initialization list很常用,效率很高。

如果是 operator=, 或是特定的 virtual function,我们在派生类的函数内部调用基类的方法,基类的方法负责基类部分的变量。

而对于友元函数,我们选择将参数类型显示转化 (explicit cast)为基类。

所以,如你所见,因为类的设计,派生类没有基类private部分的访问权限,只能通过基类的public或者protected方法实现对那部分变量的操作。这些方法,就是 接口。理解 接口 的概念与类 设计思想还需要更多的阅读与练习。加油吧。

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值