【第十三章】C++ Primer plus 的编程练习题

// classic.h -- Cd class for declaration
#ifndef __CLASSIC_H__
#define __CLASSIC_H__
// base class
class Cd  // represents a CD disk
{
private:
	char performers[50]; // 表演者
	char label[20];
	int selections;  // number of selections
	double playtime; // playing time in minutes
public:
	Cd(char * s1, char * s2, int n, double x);
	Cd(const Cd & d);
	Cd();
	virtual ~Cd();
	virtual void report() const; // reports all CD data 
	Cd & operator=(const Cd & d);
};

class Classic : public Cd
{
private:
	char masterworks[80];  // 主要作品
public:
	Classic(char * str, char * s1, char * s2, int n, double x);
	Classic(const Classic & d);
	Classic();
	virtual ~Classic();
	virtual void report() const; // reports all CD data 
	Classic & operator=(const Classic & d);
};

#endif /* __CLASSIC_H__ */

// classic.cpp -- class methods definition
#include <iostream>
#include <cstring>
#include "classic.h"

// Cd class methods
Cd::Cd(char * s1, char * s2, int n, double x)
{
	strncpy(performers, s1, 39);
	performers[39] = '\0';
	strncpy(label, s2, 19);
	label[19] = '\0';
	selections = n;
	playtime = x;
}

Cd::Cd(const Cd & d)
{
	strcpy(performers, d.performers);
	strcpy(label, d.label);
	selections = d.selections;
	playtime = d.playtime;
}

Cd::Cd()
{
	strnset(performers, 0, 40);
	strnset(label, 0, 20);
	selections = 0;
	playtime = 0;
}

Cd::~Cd()
{

}

void Cd::report() const
{ 
	std::cout << "Performers: " << performers << std::endl;
	std::cout << "Label: " << label << std::endl;
	std::cout << "Selections: " << selections 
		      << ", Playtime: " << playtime << std::endl;
}

Cd & Cd::operator=(const Cd & d)
{
	if (this == &d)
		return *this;
	strcpy(performers, d.performers);
	strcpy(label, d.label);
	selections = d.selections;
	playtime = d.playtime;
	return *this;
}

// Classic class methods
Classic::Classic(char * str, char * s1, char * s2, 
	int n, double x) : Cd(s1, s2, n, x)
{
	strncpy(masterworks, str, 79);
	masterworks[79] = '\0';
}

Classic::Classic(const Classic & d) : Cd(d)
{
	strcpy(masterworks, d.masterworks);
}

Classic::Classic() // : Cd() 不写也会调用基类的默认构造函数
{
	strnset(masterworks, 0, 80);
}

Classic::~Classic()
{

}

void Classic::report() const // 这里的const其实的含义是:const Classic *this 
{
	Cd::report();
	std::cout << "MasterWorks: " << masterworks << std::endl;
}

Classic & Classic::operator=(const Classic & d)
{
	if (this == &d)
		return *this;
	Cd::operator=(d);
	strcpy(masterworks, d.masterworks);
	return *this;
}

/***********************************
	2017年11月30日11:04:26
	Athor:xiyuan255
	Course:C++
	Contain:useclassic.cpp
			 classic.h
		     classic.cpp
	Reference: C++ Primer plus
		知识点:用virtual修饰的函数,表示该函数变成是虚函数。它的作用是
		        虚函数是使用动态联编的,而非普通函数使用的静态联编。使用
				动态联编,具体表示的是该函数将按对象的类型调用。如:一个
				基类类型的指针,但它指向基类的派生类对象时,如果是该函数
				非虚函数,则只能调用基类的该函数,如果该函数是虚函数,则
				此时将调用指向对象类型的同名该函数,即派生类的该函数。
	说明:C++ Primer plus第十三章的第一题练习题
		 【 参考 P531 】
*************************************/
// useclassic.cpp -- using Cd class methods
// compile with classic.cpp
#include <iostream>
using namespace std;
#include "classic.h"  // which will contain Cd class

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);

	cout << "Using object directly:\n";
	c1.report();   // use Cd method
	c2.report();   // use Classic method

	Cd *pcd = &c1;
	cout << "Using type cd * pointer to objects:\n";
	pcd->report();   // use Cd method for cd object
	pcd = &c2;
	pcd->report();   // use Classic method for classic object

	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;
}

/**
输出结果:
	Using object directly:
	Performers: Beatles
	Label: Capitol
	Selections: 14, Playtime: 35.5
	Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Using type cd * pointer to objects:
	Performers: Beatles
	Label: Capitol
	Selections: 14, Playtime: 35.5
	Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Calling a function with a Cd reference argument:
	Performers: Beatles
	Label: Capitol
	Selections: 14, Playtime: 35.5
	Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Testing assignment: Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
*/

void bravo(const Cd & disk)
{
	disk.report();
}

// classic2.h -- Cd class for declaration
#ifndef __CLASSIC2_H__
#define __CLASSIC2_H__
// base class
class Cd  // represents a CD disk
{
private:
	char * performers; // 表演者
	char * label;
	int selections;  // number of selections
	double playtime; // playing time in minutes
public:
	Cd(char * s1, char * s2, int n, double x);
	Cd(const Cd & d);
	Cd();
	virtual ~Cd();
	virtual void report() const; // reports all CD data 
	Cd & operator=(const Cd & d);
};

class Classic : public Cd
{
private:
	char * masterworks;  // 主要作品
public:
	Classic(char * str, char * s1, char * s2, int n, double x);
	Classic(const Classic & d);
	Classic();
	virtual ~Classic();
	virtual void report() const; // reports all CD data 
	Classic & operator=(const Classic & d);
};

#endif /* __CLASSIC2_H__ */

// classic.cpp -- class methods definition
#include <iostream>
#include <cstring>
#include "classic2.h"

// Cd class methods
Cd::Cd(char * s1, char * s2, int n, double x)
{
	performers = new char[strlen(s1) + 1];
	strcpy(performers, s1);
	performers[strlen(s1)] = '\0';

	label = new char[strlen(s2) + 1];
	strcpy(label, s2);
	label[strlen(s2)] = '\0';

	selections = n;
	playtime = x;
}

Cd::Cd(const Cd & d)
{
	performers = new char[strlen(d.performers) + 1];
	strcpy(performers, d.performers);
	performers[strlen(d.performers)] = '\0';

	label = new char[strlen(d.label) + 1];
	strcpy(label, d.label);
	label[strlen(d.label)] = '\0';

	selections = d.selections;
	playtime = d.playtime;
}

Cd::Cd()
{
	performers = new char[1];
	performers[0] = '\0';
	label = new char[1];
	label[0] = '\0';
	selections = 0;
	playtime = 0;
}

Cd::~Cd()
{
	delete[] performers;
	delete[] label;
}

void Cd::report() const
{ 
	std::cout << "Performers: " << performers << std::endl;
	std::cout << "Label: " << label << std::endl;
	std::cout << "Selections: " << selections 
		      << ", Playtime: " << playtime << std::endl;
}

Cd & Cd::operator=(const Cd & d)
{
	if (this == &d)
		return *this;

	delete[] performers;
	delete[] label;
	performers = new char[strlen(d.performers) + 1];
	strcpy(performers, d.performers);
	performers[strlen(d.performers)] = '\0';

	label = new char[strlen(d.label) + 1];
	strcpy(label, d.label);
	label[strlen(d.label)] = '\0';

	selections = d.selections;
	playtime = d.playtime;
	return *this;
}

// Classic class methods
Classic::Classic(char * str, char * s1, char * s2, 
	int n, double x) : Cd(s1, s2, n, x)
{
	masterworks = new char[strlen(str) + 1];
	strcpy(masterworks, str);
	masterworks[strlen(str)] = '\0';
}

Classic::Classic(const Classic & d) : Cd(d)
{
	masterworks = new char[strlen(d.masterworks) + 1];
	strcpy(masterworks, d.masterworks);
	masterworks[strlen(d.masterworks)] = '\0';
}

Classic::Classic() // : Cd() 不写也会调用基类的默认构造函数
{
	masterworks = NULL; // delete[]可以释放空类型指针
}

Classic::~Classic()
{
	delete[] masterworks;
}

void Classic::report() const // 这里的const其实的含义是:const Classic *this 
{
	Cd::report();
	std::cout << "MasterWorks: " << masterworks << std::endl;
}

Classic & Classic::operator=(const Classic & d)
{
	if (this == &d)
		return *this;
	delete[] masterworks;
	Cd::operator=(d);
	masterworks = new char[strlen(d.masterworks) + 1];
	strcpy(masterworks, d.masterworks);
	masterworks[strlen(d.masterworks)] = '\0';
	return *this;
}

/***********************************
	2017年11月30日14:08:02
	Athor:xiyuan255
	Course:C++
	Contain:useclassic2.cpp
			 classic2.h
		     classic2.cpp
	Reference: C++ Primer plus
		知识点:析构函数、析构函数和赋值运算符不能被继承,其中派生类
			的析构函数(如果基类和派生类的析构函数都是虚的)则会自动调用
			基类的析构函数。其中构造函数需要在初始化列表中显式调用基类
			的构造函数,如未显式调用,则调用基类的默认构造函数。其中赋值
			运算符的操作需要显式调用基类的赋值运算操作函数。
			2.当派生类中使用new分配内存的话,则需要在派生类中重新实现析
			  构函数、复制构造函数和赋值运算符的操作。
	说明:C++ Primer plus第十三章的第二题练习题
		 【 参考 P531 】
*************************************/
// useclassic2.cpp -- using Cd class methods
// compile with classic2.cpp
#include <iostream>
using namespace std;
#include "classic2.h"  // which will contain Cd class

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);

	cout << "Using object directly:\n";
	c1.report();   // use Cd method
	c2.report();   // use Classic method

	Cd *pcd = &c1;
	cout << "Using type cd * pointer to objects:\n";
	pcd->report();   // use Cd method for cd object
	pcd = &c2;
	pcd->report();   // use Classic method for classic object

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

	cout << "Testing assignment: ";
	Classic copy;
	copy = c2;
	copy.report();
	cout << "Done!\n";

	return 0;
}

/**
输出结果:
	Using object directly:
	Performers: Beatles
	Label: Capitol
	Selections: 14, Playtime: 35.5
	Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Using type cd * pointer to objects:
	Performers: Beatles
	Label: Capitol
	Selections: 14, Playtime: 35.5
	Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Calling a function with a Cd reference argument:
	Performers: Beatles
	Label: Capitol
	Selections: 14, Playtime: 35.5
	Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Testing assignment: Performers: Alfred Brendel
	Label: Philips
	Selections: 2, Playtime: 57.17
	MasterWorks: Piano Sonata in B flat, Fantasia in-C
	Done!
*/

void bravo(const Cd & disk)
{
	disk.report();
}

#pragma once
/* programNo13.14 */
// dma.h -- inheritance and dynamic memory allocation
#ifndef __DMA2_H__
#define __DMA2_H__

class DMA
{
private:
	char * label;
	int rating;
public:
	DMA(const char * l = "null", int r = 0); // 默认构造函数 default constructor function
	DMA(const DMA & rs); // 复制构造函数 copy constructor function
	virtual ~DMA();  // 虚析构函数 virtual destructor function
	char * getLabel() const { return label; }
	int getRating() const { return rating; }
	DMA & operator=(const DMA & rs); // 运算符的重载 overload operator
	virtual void view() const = 0;   // '=0'纯虚函数,代表该类属于抽象类,无法生成对象
};

// Base Class Using DMA
class BaseDMA : public DMA
{
public:
	/* 这里没有定义析构函数,但该类对象被释放时,仍会调用析构函数;
	 * 这个析构函数不是从基类继承过来的,因为析构函数是不能被继承的。
	 * 这个析构函数是由于用户没有定义,而由编译器自动生成的,只是这个
	 * 析构函数不执行任何操作。 */
	BaseDMA(const char * l = "null", int r = 0) : DMA(l, r) { } // 默认构造函数 default constructor function
	BaseDMA(const BaseDMA & rs) : DMA(rs) { } // 复制构造函数 copy constructor function
	BaseDMA & operator=(const BaseDMA & rs); // 运算符的重载 overload operator
	virtual void view() const;  // 虚函数,代表该函数可以按对象类型调用
};

// derived class without DMA
// no destructor needed              // 不需要析构函数
// uses implicit copy constructor    // 使用隐式的复制构造函数
// uses implicit assignment operator // 使用隐式的赋值运算符操作
class LacksDMA : public DMA
{
private:
	enum { COL_LEN = 40 };
	char color[COL_LEN];
public:
	LacksDMA(const char * c = "blank", const char * l = "null", int r = 0);
	LacksDMA(const char * c, const BaseDMA & rs);
	virtual void view() const;
};

// derived class with DMA
class HasDMA : public DMA
{
private:
	char * style;
public:
	HasDMA(const char * s = "none", const char * l = "null", int r = 0);
	HasDMA(const char * s, const BaseDMA & rs);
	HasDMA(const HasDMA & hs);
	~HasDMA();
	HasDMA & operator=(const HasDMA & hs);
	virtual void view() const;
};


#endif /* __DMA2_H__ */

// dma2.cpp -- dma class methods
#include <iostream>
#include "dma2.h"
#include <cstring>

// DMA methods
DMA::DMA(const char * l, int r)
{
	label = new char[std::strlen(l) + 1];
	std::strcpy(label, l);
	label[std::strlen(l)] = '\0';
	rating = r;
}

DMA::DMA(const DMA & rs)
{
	label = new char[std::strlen(rs.label) + 1];
	std::strcpy(label, rs.label);
	label[std::strlen(rs.label)] = '\0';
	rating = rs.rating;
}

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

DMA & DMA::operator=(const DMA & rs)
{
	if (this == &rs)
		return *this;
	delete[] label;
	label = new char[std::strlen(rs.label) + 1];
	std::strcpy(label, rs.label);
	label[std::strlen(rs.label)] = '\0';
	rating = rs.rating;
	return *this;
}

// BaseDMA methods
BaseDMA & BaseDMA::operator=(const BaseDMA & rs)
{
	if (this == &rs)
		return *this;
	DMA::operator=(rs);
	return *this;
}

void BaseDMA::view() const
{
	std::cout << "Label: " << getLabel() << std::endl;
	std::cout << "Rating: " << getRating() << std::endl;
}

// lacksDMA methods
LacksDMA::LacksDMA(const char * c, const char * l, int r) : DMA(l, r)
{
	//std::cout << "LacksDMA" << c << std::endl;
	std::strcpy(color, c);
	color[std::strlen(c)] = '\0';
}

LacksDMA::LacksDMA(const char * c, const BaseDMA & rs) : DMA(rs)
{
	std::strcpy(color, c);
	color[std::strlen(c)] = '\0';
}

void LacksDMA::view() const
{
	std::cout << "Label: " << getLabel() << std::endl;
	std::cout << "Rating: " << getRating() << std::endl;
	std::cout << "Color: " << color << std::endl;
}

// HasDMA  methods
HasDMA::HasDMA(const char * s, const char * l, int r) : DMA(l, r)
{ // 因为是先有了基类,而后采用了派生类,所以不能在派生类创建对象完成后才对基类的
  // 成员进行赋值。所以才创建的一条新的语法,叫做列表初始化语句来创建基类对象和初
  // 始化基类成员。如本语句的 : DMA(l, r)。
	//std::cout << "HasDMA" << s << std::endl;
	style = new char[std::strlen(s) + 1];
	std::strcpy(style, s);
	style[std::strlen(s)] = '\0';
}

HasDMA::HasDMA(const char * s, const BaseDMA & rs) : DMA(rs)
{  // 由于HasDMA是BaseDMA基类的派生类,则基类的引用可以指向派生类的引用。所以可以
   // 直接将派生类的引用直接发生给基类的构造函数,用于创建基类对象及初始化。
	style = new char[std::strlen(s) + 1];
	std::strcpy(style, s);
	style[std::strlen(s)] = '\0';
}

HasDMA::HasDMA(const HasDMA & hs) : DMA(hs)
{ // 派生类创建对象时,是先创建基类的对象所需内存空间,然后再创建派生类自己特有成员
  // 所需的内存空间。所以虽然派生类无法直接访问基类的私有成员,但却有这些私有成员所
  // 占的内存空间。
	style = new char[std::strlen(hs.style) + 1];
	std::strcpy(style, hs.style);
	style[std::strlen(hs.style)] = '\0';
}

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

HasDMA & HasDMA::operator=(const HasDMA & hs)
{
	if (this == &hs)
		return *this;
	DMA::operator=(hs); // 这条语句含义是*this = hs;(目的是调用基类的赋值运算符操作函数)
	         // 但如果这样*this = hs;写的话,对于编译器它无法理解我们的本意,只会调用
	         // HasDMA::operator=(hs),则将造成这个函数出现递归死循环。所以这里用显式的方式
	         // 调用基类的重载赋值运算符函数。
	delete[] style;
	style = new char[std::strlen(hs.style) + 1];
	std::strcpy(style, hs.style);
	return *this;
}

void HasDMA::view() const
{
	std::cout << "Label: " << getLabel() << std::endl;
	std::cout << "Rating: " << getRating() << std::endl;
	std::cout << "Style: " << style << std::endl;
}

/***********************************
	2017年12月1日10:14:00
	Athor:xiyuan255
	Course:C++
	Contain:usedma2.cpp
			 dma2.h
		     dma2.cpp
	Reference: C++ Primer plus
		知识点:
			1.基类中的析构函数、构造函数和赋值运算符不能被继承,所以
			  需要在派生类重新定义。
			2.编译器会自动生成一些特殊的成员函数,如下:
			  (1) 默认构造函数,如果用户没有定义构造函数的话;
			  (2) 复制构造函数,如果用户没有定义复制构造函数的话;
			  (3) 析构函数,如果用户没有定义析构函数的话;
			  (4) 赋值运算符操作,如果用户没有定义赋值运算符操作的话;
			3.当派生类中的成员没有需要用new分配空间时,用户可以不重新
			  实现析构函数、赋值运算符和复制构造函数,只要使用编译器
			  自动生成的即可满足要求。
			  (1) 因为编译器自动生成的析构函数是没有进行任何操作,但对象
			      没释放掉,对象的非new分配的空间也就被释放掉,则满足要求;
			  (2) 因为编译器自动生成的赋值运算符操作,默认是按对象成员赋值的,
			      属于浅复制,因没有new分配的内存,则浅复制以满足需求;
			  (3) 因为编译器自动生成的复制构造函数,默认是按对象成员赋值的,
			      属于浅复制,因没有new分配的内存,则浅复制以满足需求;
             注: 如果派生类中的成员有用new分配空间时,用户必须重新实现析构函数、
			 赋值运算符和复制构造函数。
	说明:C++ Primer plus第十三章的第三题练习题
		 【 参考 P531 】
*************************************/
// usebrass2.cpp -- polymorphic example
// compile with brass.cpp
#include <iostream>
#include "dma2.h"
const int CLIENTS = 3;

int main()
{
	using std::cin;
	using std::cout;
	using std::endl;

	DMA * p_clients[CLIENTS];
	char temp[40];
	int rating;
	char kind;

	for (int i = 0; i < CLIENTS; i++) {
		cout << "Enter label vlaue: ";
		cin.getline(temp, 40); 
		cout << "Enter rating vlaue: ";
		cin >> rating;     
		cout << "Enter 1 for BaseDMA or "
			 << "2 for LacksDMA and 3 for HasDMA: ";

		while (cin >> kind && (kind != '1' && kind != '2' && kind != '3'))
			cout << "Enter either 1 , 2, or 3: ";

		while (cin.get() != '\n')
			continue;

		if ('1' == kind)
			p_clients[i] = new BaseDMA(temp, rating);
		else if ('2' == kind) {
			char color[40];
			cout << "Enter color vlaue: ";
			cin.getline(color, 40); 
			p_clients[i] = new LacksDMA(color, temp, rating);
		} else {
			char style[40];
			cout << "Enter style vlaue: ";
			cin.getline(style, 40); 
			p_clients[i] = new HasDMA(style, temp, rating);
		}
		
	}
	cout << endl;

	for (int i = 0; i < CLIENTS; i++) {
		p_clients[i]->view(); // 因为view是虚函数,所以p_clients[i]->view()
		            // 指针指向的成员函数是按对象类型调用,而不会按指针类型调用
		cout << endl;
	}
	/* 如果不加如下代码,只会释放DMA * p_clients[CLIENTS]的数组的指针
	 * 成员,但不会释放指针成员所指向的对象,因为他们是在new中分配的 */
	for (int i = 0; i < CLIENTS; i++) {
		delete p_clients[i];  // free memory
	}
	cout << "Done.\n";

	return 0;
}

/**
输出结果:
	Enter label vlaue: 11223344
	Enter rating vlaue: 5
	Enter 1 for BaseDMA or 2 for LacksDMA and 3 for HasDMA: 1
	Enter label vlaue: 22334455
	Enter rating vlaue: 8
	Enter 1 for BaseDMA or 2 for LacksDMA and 3 for HasDMA: 2
	Enter color vlaue: red
	Enter label vlaue: 33445566
	Enter rating vlaue: 3
	Enter 1 for BaseDMA or 2 for LacksDMA and 3 for HasDMA: 3
	Enter style vlaue: unkown

	Label: 11223344
	Rating: 5

	Label: 22334455
	Rating: 8
	Color: red

	Label: 33445566
	Rating: 3
	Style: unkown

	Done.
*/

// port.h -- class Port decalaration
#ifndef __PORT_H__
#define __PORT_H__
#include <iostream>
using namespace std;
class Port
{
private:
	char * brand; // 品牌
	char style[20];  // i.e., tawny, ruby, vintage
	int bottles;  // 葡萄酒的瓶数
public:
	Port(const char * br = "none", const char * st = "none", int b = 0);
	Port(const Port & p);               // copy constructor
	virtual ~Port() { delete[] brand; }
	Port & operator=(const Port & p);
	Port & operator+=(int b);           // adds b to bottles
	Port & operator-=(int b);           // subtracts b fromto bottles, if available
	int bottleCount() const { return bottles; }
	virtual void show() const;
	friend ostream & operator<<(ostream & os, const Port & p);
};

class VintagePort : public Port  // style necessarily = "vintage"
{
private:
	char * nickname;   // i.e., "The Noble" or "Old Velvet", etc.
	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);
};

#endif /* __PORT_H__ */

// port.cpp -- class Port methods definition
#include "port.h"
#include <cstring>

// Port class methods
Port::Port(const char * br, const char * st, int b)
{
	brand = new char[std::strlen(br) + 1];
	strcpy(brand, br);
	brand[std::strlen(br)] = '\0';

	strncpy(style, st, std::strlen(st));
	style[std::strlen(st)] = '\0';
	
	bottles = b;
}

Port::Port(const Port & p)
{
	brand = new char[std::strlen(p.brand) + 1];
	strcpy(brand, p.brand);
	brand[std::strlen(p.brand)] = '\0';

	strncpy(style, p.style, std::strlen(p.style));
	style[std::strlen(p.style)] = '\0';
	
	bottles = p.bottles;
}

Port & Port::operator=(const Port & p)
{
	if (this == &p)
		return *this;
	delete[] brand;
	brand = new char[std::strlen(p.brand) + 1];
	strcpy(brand, p.brand);
	brand[std::strlen(p.brand)] = '\0';

	strncpy(style, p.style, std::strlen(p.style));
	style[std::strlen(p.style)] = '\0';
	
	bottles = p.bottles;
	return *this;
}

Port & Port::operator+=(int b)
{
	if (b < 0) 
		cout << "不能加上葡萄酒的瓶数为负的数!\n";
    else 
		bottles += b;
	return *this;
}

Port & Port::operator-=(int b)
{
	if (b > bottles) 
		cout << "不能减去大于剩余葡萄酒的瓶数的数!\n";
	else if (b < 0)
		cout << "不能减去葡萄酒的瓶数为负的数!\n";
	else
		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;
}

// VintagePort class methods
VintagePort::VintagePort() // 没有显式调用,则调用基类的默认构造函数,
{  // 即,Port(const char * br = "none", const char * st = "none", int b = 0)
	nickname = NULL;
	year = 0;
}

VintagePort::VintagePort(const char * br, int b, 
	const char * nn, int y) : Port(br, "vintage", b)
{
	nickname = new char[std::strlen(nn) + 1];
	strcpy(nickname, nn);
	nickname[std::strlen(nn)] = '\0';
	year = y;
}

VintagePort::VintagePort(const VintagePort & vp) : Port(vp)
{
	nickname = new char[std::strlen(vp.nickname) + 1];
	strcpy(nickname, vp.nickname);
	nickname[std::strlen(vp.nickname)] = '\0';
	year = vp.year;
}

VintagePort & VintagePort::operator=(const VintagePort & vp)
{
	if (this == &vp)
		return *this;
	Port::operator=(vp);

	delete[] nickname;
	nickname = new char[std::strlen(vp.nickname) + 1];
	strcpy(nickname, vp.nickname);
	nickname[std::strlen(vp.nickname)] = '\0';
	year = vp.year;
	return *this;
}

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

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

/***********************************
	2017年12月1日11:45:21
	Athor:xiyuan255
	Course:C++
	Contain:useport.cpp
			 port.h
		     port.cpp
	Reference: C++ Primer plus
		知识点:
			1.operator=()没有声明为虚,是因为赋值运算符不能被继承,
			  所以每个类都应有自己独立的赋值运算符函数;定义为虚的
			  前提是该函数能被继承,这样定义为虚才有意义,因为如果
			  能被继承的函数被定义为虚的,就可以实现按对象类型调用
			  继承成员函数,而不仅仅的基类成员函数。
			2.oerator<<()是因为它是友元函数,友元函数不属于类成员,
			  更不用说继承的。
			3.Port & operator+=(int b);           // adds b to bottles
			  Port & operator-=(int b);           // subtracts b fromto bottles, if available
			  int bottleCount() const { return bottles; }
			  这三个函数,派生类没有重新实现,因为派生类要执行的操作
			  与基类一样,所以直接继承过来使用就行。
			4.virtual void show() const 因为派生类要实现的内容与基类
			  不同,所以需要重新定义。之所以定义为虚,是因为在指针返回
			  时,可以实现按对象类型访问而不是按指针类型访问成员函数。
			5.赋值运算符操作成员函数之所以不能被继承,是因为它的形参是
			  特定类类型的参数,与基类不同,所以无法继承。
	说明:C++ Primer plus第十三章的第四题练习题
		 【 参考 P531 】
*************************************/
// useport.cpp -- Using class Port and VintagePort methods
// compile with port.h
#include "port.h"

int main()
{
	Port * ptr;

	Port p("Gallo", "tawny", 20);
	p += 20;
	ptr = &p;
	ptr->show();
	
	VintagePort v1("Gallo", 40, "zhangsan", 1987);
	VintagePort v2("China", 40, "zhaoliu", 1963);
	v1 -= 20;
	ptr = &v1;
	ptr->show();

	VintagePort v3 = v1;
	std::cout << v3 << std::endl;

	Port p1;
	p1 = v2;
	std::cout << p1 << std::endl;

	VintagePort v4; 
	v4 = v2;
	v4.show();

	return 0;
}

/**
输出结果:
	Brand: Gallo
	Kind: tawny
	Bottles: 40
	Brand: Gallo
	Kind: vintage
	Bottles: 20
	Nickname: zhangsan
	Year: 1987
	Gallo, vintage, 20, zhangsan, 1987
	China, vintage, 40
	Brand: China
	Kind: vintage
	Bottles: 40
	Nickname: zhaoliu
	Year: 1963
*/

1.静态联编和动态联编
程序调用函数时,将使用哪个可执行代码块呢?编译器负责回答这个问题。将源代码中的函数调用解释
为执行特定的函数代码块被称为函数名联编(binding)。在C语言中,这非常简单,因为每个函数名都对
应一个不同的函数。但在C++中,由于函数重载的缘故,这项任务更加复杂。编译器必须查看函数参数
以及函数名才能确定使用哪个函数。然而,C/C++编译器可以在编译过程完成这种联编。这编译过程中进
行联编被称为静态联编(static binding),又称早期联编(early binding)。然而,虚函数使用这项工作
变得更困难。正如程序清单13.10所示那样,使用哪一个函数是不能在编译时确定的,因为编译器不知道
用户将选择哪种类型对象。所以,编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为
动态联编(dynamic binding),又称为晚期联编(late binding)。
2.将派生类引用或指针转换为基类引用或指针被称为向上强制转换(upcasting),这使公有继承不需要进行
显式类型转换。向上强制类型转换是可传递的。
3.将基类指针或引用转换为派生类指针或引用被称为向下强制转换(downcasting)。如果不使用显式类型转
换,则向下强制转换是不允许的。
4.隐式向上强制转换使基类指针或引用可以指向基类对象或派生类对象,因为需要动态联编。C++使用虚成
员函数来满足这种需求。
5.如下代码:
  BrassPlus ophelia; 
  Brass * bp;
  bp = &ophelia; 
  bp->viewAcct();
  (1) 如果在基类中没有将viewAcct()声明为虚的,则bp->viewAcct()将根据指针类型(Brass *)调用Brass
      ::viewAcct()。指针类型在编译时已知,因此编译器在编译时,可以将viewAcct()关联到Brass::viewAcct()。
      总之,编译器对非虚方法使用静态联编。
  (2) 如果在基类中将viewAcct()声明为虚的,则bp->viewAcct()将根据对象类型(BrassPlus)调用BrassPlus::
      viewAcct()。在这个例子中,对象类型为BrassPlus,但通常只有在程序运行时,才能确定此时指针指向
      的对象类型。总之,编译器对虚方法使用动态联编。
6.如果要在派生类中重新定义基类方法,则将它设置为虚方法;否则,设置为非虚方法。
7.关于虚函数的注意事项:
  (1) 构造函数不能是虚函数。因为构造函数不能被继承,派生类使用的是自己的构造函数然后使用的基类的构造函数
      来初始化,但这不同于继承机制。
  (2) 析构函数应当是虚函数,除非类不做基类。因为如果基类指针指向派生类时,使用delete将只调用指针类型的
      类对象的析构函数,但如果是虚析构函数,则将先调用指针指向对象的派生类的析构函数,再由派生类的析构
      函数调用基类的析构函数。
  (3) 有元函数不能是虚函数,因为有元不是类成员,而只有类成员才能是虚函数。
  (4) 如果派生类没有重新定义该函数,则使用该函数的基类版本。
  (5) 在派生类中重新定义从基类继承过来的方法,则在派生类中将只存在重新定义的版本,而隐藏同名的基类方法。
      这说明在派生类中的重新定义(或叫重新实现),不属于函数的重载。
      a.如果重新定义继承方法,应确保与原来的原型完成相同,但如果返回值是基类引用或指针时,返回值需改成
        指向派生类的引用或指针。
      b.如果基类声明的方法被重载了,则应在派生类中重新定义所有基类的版本。因为只重新定义重载的一个版本,
        则其他重载版本将被隐藏,派生类对象将无法使用它们。
8.访问控制符:protected 在相对于外部类外部而言,关键字protected与关键字private相似,在类外只能用公有类
  成员来访问protected的类成员。而protected和private之间的区别只有在基类派生的类中才会表现出来。派生类成
  员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。因此,对于外部世界来说,保护成员的行为与私
  有成员相似;但对于派生类来说,保护成员的行为与公有成员相似。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值