第十五章之(二)嵌套类

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

在C++中,可以将类声明放在另一个类中。

在另一个类中声明的类,被称为 嵌套类(nested class     

 

它通过提供新的类型类作用域类避免名称混乱。

 

包含类的成员函数,可以创建和使用被嵌套类的对象。

 

而仅当声明位于公有部分时,才能在包含类的外面使用嵌套类。而且必须使用作用域解析运算符(旧版本可能不允许使用嵌套类,或无法完全实现这种概念)。

 

不懂,感觉是一个类A的类声明在另外一个类B里(此时,这个A类的声明,即可能在B的公有部分,也可能在私有部分,还可能在保护部分。),只有当类A的声明在类B的公有部分,类A才能在类B外面使用(即类似数据成员位于公有部分)。并且要加作用域解析运算符才能调用A

 

有点类似在类中声明一个结构。

 

对类进行嵌套,与包含并不相同。包含意味着将类对象作为另一个类的成员(但需要在类定义里面进行声明,并且声明几个就是几个,除非使用new来分配,但这需要一个指针)。而对类进行嵌套不创建类成员,而是定义了一种类型。该类型仅在包含嵌套类声明的类中有效(也就是说,只能在该类方法中声明此类)。

 

 

嵌套和包含的区别:

从以上说明来看:

包含一般是固定数量的类对象,或者使用new来动态分配,但就算使用new来动态分配,其指针的数量是一定的(在类定义时确认)。但由于在每个类方法中都可以使用该指针,因此在不同类方法中,可以共享同一个数据。

嵌套,可以自由在类方法中声明类对象,如果该类是自动变量的话,那么离开该类方法时,会自动销毁(自动变量的特性)。因此,一个类方法中声明的嵌套类的对象,不能在另一个类方法中使用(因为其作用域是该类方法)。

 

 

 

假如在一个类中声明另一个类,文中使用的是队列模拟(特点是队首删除,队尾添加)。之前使用的是结构形式(一个成员为项目,即队列里的东西,另一个成员为指针,指向下一个结构的位置)

现就将结构改为类的形式,进行推断:

①首先,数据成员保持不变,即一个数据成员为项目,另一个数据成员为指针。但此时,指针的类型更改,变为该类的指针;

 

②当添加新对象时(队尾添加),则需要创建一个新的项目,因此需要通过类对象来创建。

由于项目在类中,因此可以调用类的构造函数,在创建类对象的时候,同时创建该项目。又因为项目类型不定(比如说可能是某个类),因此,在构造函数中使用成员初始化列表。(这样,假如项目是类,将自动调用其对应构造函数)。

因此,我们需要自定义类的构造函数。

另外,这个构造函数,要能将指针自动指向空指针(因为新加入的便是最后一个)

 

③其他变化不大,每进来一个,则要new一个新的(并且赋值)。每结束一个,则调用delete(与new对应)

 

代码如下(用之前的ATM模拟测试):

#include<iostream>
#include<ctime>

class Customer
{
	long arrive;	//到达时间
	int processtime;	//自身消耗时间
public:
	Customer() { arrive = processtime = 0; }	//默认构造函数,初始化
	long when()const { return arrive; }	//返回到达时间
	int ptime()const { return processtime; }	//返回消耗时间
	void set(long when)	//将到达时间作为参数传递
	{
		processtime = std::rand() % 3 + 1;	//等待1~4单位时间
		arrive = when;	//到达时间等于when
	}
};

typedef Customer Item;
class Queue
{
private:
		//先声明一个类
	class Node
	{
	public:
		Item item;
		Node* next;
		Node(const Item&it) :item(it) { next = NULL; }	//默认为空指针,将项目传递给私有数据成员
	};
	int items = 0;	//默认情况下,size(当前项目数)为0
	const int MAX;	//最大容纳
	Node *front;	//指向第一个
	Node *rear;	//指向最后一个
public:
	Queue(int m = 10);
	~Queue();
	bool isFull()const { return items == MAX; }	//如果等于最大则满
	bool isEmpty()const { return items == 0; }	//如果等于0则空
	int QueueCount() { return items; }	//返回数目
	bool enqueue(const Item&it);	//放入
	bool dequeue(Item&it);	//取出
};

Queue::Queue(int m) :MAX(m)	//构造函数,指针默认为空指针,默认情况下,MAX的值为10
{
	front = NULL;
	rear = NULL;
}
Queue::~Queue()
{
	while (front != NULL)	//如果第一个指针指向的不是空(说明不是最后一个
	{
		Node*temp = front;	//临时指针
		front = front->next;	//front指向下一个
		delete temp;	//删除当前对象(new出来的)
	}
}
bool Queue::enqueue(const Item&it)
{
	if (isFull())return false;
	Node*add = new Node(it);	//new一个动态对象(否则就成了自动变量了)
	if (front == NULL)		//如果front是空,说明这是加入的第一个,否则front必然指向第一个(那就不是空)
		rear = front = add;	//如果是新对象,那么rear和front都应该指向当前对象
	else		//否则加入的不是第一个
	{
		rear->next = add;	//上一个Node的指针指向新创建的对象(最后一个)
		rear = add;	//指向最后一个的指针,指向新创建的
	}
	items++;
	return true;
}
bool Queue::dequeue(Item&it)
{
	if (isEmpty())return false;
	Node*temp = front;
	front = front->next;	//指向第一个的指针,指向下一个对象(删除后的第一个)
	delete temp;	//删除原来的第一个对象
	if (front == NULL)	//如果front是空指针(说明刚刚删除的是最后一个对象)
		rear = front;	//那么rear也应该是空指针
	items--;
	return true;
}
const int MIN_PER_HR = 60;

bool newcustomer(double x);

int main()
{
	using namespace std;
	srand(time(0));

	cout << "Case Study: Bank of Heather Automatic Teller\n";
	cout << "Enter maximum size of queue: ";
	int qs;
	cin >> qs;
	Queue line(qs);

	cout << "Enter the number of simulation hours: ";
	int hours;
	cin >> hours;
	long cyclelimit = MIN_PER_HR*hours;
	cout << "Enter the average number of customer per hour: ";
	double perhour;
	cin >> perhour;
	double min_per_cust;
	min_per_cust = MIN_PER_HR / perhour;

	Item temp;
	long turnaways = 0;
	long customers = 0;
	long served = 0;
	long sum_line = 0;
	int wait_time = 0;
	long line_wait = 0;

	for (int cycle = 0; cycle < cyclelimit; cycle++)
	{
		if (newcustomer(min_per_cust))
		{
			if (line.isFull())
				turnaways++;
			else
			{
				customers++;
				temp.set(cycle);
				line.enqueue(temp);
			}
		}
		if (wait_time <= 0 && !line.isEmpty())
		{
			line.dequeue(temp);
			wait_time = temp.ptime();
			line_wait += cycle - temp.when();
			served++;
		}
		if (wait_time > 0)
			wait_time--;
		sum_line += line.QueueCount();
	}
	if (customers > 0)
	{
		cout << "customers accepted: " << customers << endl;
		cout << "  customers served: " << served << endl;
		cout << "         turnaways: " << turnaways << endl;
		cout << "average queue size: ";
		cout.precision(2);
		cout.setf(ios_base::fixed, ios_base::floatfield);
		cout << (double)sum_line / cyclelimit << endl;
		cout << " average wait time: " << (double)line_wait / served << " minutes\n";
	}
	else
		cout << "No customers!\n";
	cout << "Done!\n";
	system("pause");
	return 0;
}

bool newcustomer(double x)
{
	return (std::rand()*x / RAND_MAX < 1);
}

测试结果与之前相同,说明代码正常运行。

 

 

调用嵌套类中的方法:

假如Node是一个普通的类,当我们调用它的方法时,是这样做的:Node::isFull();

 

而此时,Node类在Queue类中,因此,我们调用Node类方法时,则不能像之前那样,但方法类似,加上新的作用域解析运算符,即:Queue::Node::isFull();即可。(前提是,Node类在Queue类的public部分声明)

 

 

同样,假如需要在类外声明一个在类内public区域声明的类,需要在类型名前加上作用域解析运算符。例如Queue::Node one; 这样。

 

但在private和protected区域声明的类,则无法做到这点(即无法在类外使用)。

protected可以在派生类使用,而private只能在当前类使用。

 

 

 

将嵌套类放在模板类之中:

将嵌套类放在模板类之中,很简单,和使用typedef类似。

即使用模板类的<class XX>中的XX作为其类型名。

例如Node类的声明不变(其项目类型为Item),修改Queue类声明:

template<classItem>

class Queue

{

         classNode

         {

                   Item item;

                   ...

         };

         ....

};

 

之后,按照使用模板类的方法,逐个修改Queue类的类方法的函数定义即可。

 

 


目录
相关文章
|
5月前
|
设计模式 uml UED
乱用继承导致的类爆炸
摘要(Markdown格式): 了解**复杂度守恒定律**,源于1984年,指出应用的复杂性无法消除,只能转移。在探究设计模式时,发现了**桥接模式**。桥接模式通过组合而非继承处理多维度变化,避免类爆炸问题。当图形颜色和类型变化时,原本的抽象类和实现类会导致大量类产生。通过桥接模式优化,将颜色和形状解耦,实现了更灵活的结构。
|
5月前
|
Java
Java面向对象特征(二)----- 继承
Java面向对象特征(二)----- 继承
Java面向对象特征(二)----- 继承
|
6月前
|
Java
Java内部类
Java内部类
39 2
|
6月前
|
Java
什么是Java内部类,为什么使用它?
【4月更文挑战第13天】
128 1
|
6月前
|
设计模式 Java
JAVA内部类
JAVA内部类
24 1
|
6月前
|
存储 C++
第十一章 C++成员函数与静态成员详解
第十一章 C++成员函数与静态成员详解
41 0
|
NoSQL Java 编译器
你不知道的Java内部类
你不知道的Java内部类
|
C++
第十三章 类继承
第十三章 类继承
106 0
|
Java 机器人
什么是java内部类
什么是java内部类
106 0