文章目录
- 嵌套类(新增):==在其它类中声明==的类
- 为什么要设计嵌套类,有什么用?(成全其包含类)
- 从类中定义结构体引入
- 作用域和访问控制
- 示例:用模板类实现队列
- 总结
嵌套类(新增):
在其它类中声明的类
nested class
在一个类的内部定义另一个类,前者叫做包含类,后者叫做嵌套类。以前C++不支持嵌套类,是新增的(不确定是不是C++11增的。)。
包含和嵌套类可不同哈。包含是说:把一个类的对象作为另一个类的成员。而嵌套类不是说什么对象,而是创建了一个新的类!只不过这个类只在它的包含类中有效,外部要用也不是不可能,后面会讲访问控制。
为什么要设计嵌套类,有什么用?(成全其包含类)
其实设计嵌套类,主要是为了成全包含类,即是为了辅助包含类的实现。后面一点点就会明白。
- 包含类的成员函数可以创建和使用嵌套类的对象,不需要加类名限定符。
从类中定义结构体引入
其实嵌套类的形式我们早就见过了,那就是在类中声明结构体的时候,这是一种变相的嵌套类,因为结构体是一种成员默认为公有public的类。
比如,队列类,这种容器类,需要一个用结构体表示的结点,一般我们都是用的结构体,其实这就是一种嵌套类。栈,链表的实现也是一样。
class Queue
{
private:struct Node{Item item; struct Node * next;};···
};
但是这种嵌套类,并没有真正利用类的功能,即利用类的构造函数去操作。比如吧元素加入到队列中的EnQueue操作:
bool Queue::EnQueue(const Item & item)
{if (isfull())return false;Node * add = new Node;add->item = item;add->next = NULL;···
}
使用NULL的前提是包含了定义NULL的头文件,不然可以直接用0,或者C++11新增的nullptr
可以看到,Node结构体的成员是可以直接被外部赋值的,这就是因为结构体的成员默认是公有的。但是,给成员赋值这种事情,其实应该是构造函数来做才最合适,至少对于真的类来说是这样的。所以我们把Node结构体改为类,并给他写显式的构造函数:
class Queue
{class Node{public:Item item; struct Node * next;Node(const Item & i):item(i), next(0){}};···
};
这时候EnQueue函数就可以改为:
bool Queue::EnQueue(const Item & item)
{if (isfull())return false;Node * add = new Node(item);···
}
从这个代码也可以看到,Queue类(包含类)的成员函数可以创建和使用嵌套类的对象,不需要加类名限定符。
如果Node类的构造函数的定义不写在Queue类的Node类内部,而是写在单独的方法文件,那就需要加两重类名限定符,以说明Node类是在Queue类中定义的。
Queue::Node::Node(const Item & i):item(i), next(0){}
作用域和访问控制
嵌套类的作用域是包含它的类
所以在包含类外部使用,必须要用类名限定符。
不在作用域内使用,就要用类名限定符。
嵌套类的作用域和可见性是由它被声明的位置决定的,具体来说可以分为:
- 当嵌套类定义在包含类的public部分时,可以在包含类的外部通过两层类名限定符来使用嵌套类;包含类的派生类当然也可以继承嵌套类。
- 而定义在私有部分或者保护部分,包含类的外部就根本看不到嵌套类的存在,也没法使用;
- 如果定义在私有部分,则包含类的派生类也无法得知嵌套类的存在,也无法继承到;只有包含类的对象可以使用嵌套类的对象以及指向嵌套类对象的指针和引用,别人都用不了。(爸爸金屋藏娇的小三的财产怎么可能被原配的孩子继承呢)。
派生类不可以访问类的私有部分。
- 如果定义在保护部分,则包含类的派生类可以继承到嵌套类。派生类可以创建父类中的嵌套类的对象。
其实嵌套结构和嵌套枚举的作用域也是这样,是包含他们的外层的结构和枚举。
访问控制规则
要注意,嵌套类这种形式,并没有让包含类对嵌套类的访问有什么特权,也没有让嵌套类对包含类的访问有什么特权。包含类仍然只可以访问到嵌套类的公有成员。
所以一般包含类会把自己的嵌套类设置为私有成员,然后嵌套类的所有成员都是公有的,这样嵌套类就可以完全为包含类所用。
示例:用模板类实现队列
#ifndef _QUEUETP_H_
#define _QUEUETP_H_template<class Item>
class QueueTp
{class Node{public:Item item;Node * next;Node(Item it &#61; 0, Node * ne &#61; 0):item(it), next(ne){}};enum {Q_SIZE &#61; 10};Node * front;Node * rear;int items;const int qsize;QueueTp(const QueueTp & q):qsize(0){}QueueTp & operator&#61;(const QueueTp & q){return *this;}public:QueueTp(int qs &#61; Q_SIZE);~QueueTp();bool isempty() const {return items&#61;&#61;0;}bool isfull() const {return items&#61;&#61;qsize;}int queuecount() const {return items;}bool enqueue(const Item & item);bool dequeue(Item & item);
};
template<class Item>
QueueTp<Item>::QueueTp(int qs):qsize(qs)
{front &#61; rear &#61; 0;items &#61; 0;
}template<class Item>
QueueTp<Item>::~QueueTp()
{Node * node;while (front){node &#61; front;front &#61; front->next;delete node;}
}template<class Item>
bool QueueTp::enqueue(const Item & item)
{if (isfull())return false;Node * add &#61; new Node(item);&#43;&#43;items;if (!front)front &#61; add;elserear->next &#61; add;rear &#61; add;return true;
}template<class Item>
bool QueueTp::dequeue(Item & item)
{if (isempty())return false;item &#61; front->item;Node * node &#61; front;front &#61; front->next;delete node;--items;if (items&#61;&#61;0)rear &#61; 0;return true;
}
#endif
由于Node类里面的item是Item类型的&#xff0c;即通用类型。
所以如果你用char代替Item&#xff0c;那么Node类实际上就是QueueTp::Node
类&#xff1b;
如果用int代替Item,则Node类实际是QueueTp::Node
类。
这两个Node类是在两个独立的QueueTp类中定义的&#xff0c;完全不会有名称冲突。
测试程序
#include
#include
#include "queuetp.h"int main()
{using std::cout;using std::cin;using std::string;QueueTp<string> cs(5);string temp;while (!cs.isfull()){cout << "Please enter your name. ""You will be served in the order of ""arrival.\nname: ";getline(cin, temp);cs.enqueue(temp);}cout << "The queue is full. Processing begins!\n";while (!cs.isempty()){cs.dequeue(temp);cout << "Now processing " << temp << "···\n";}return 0;
}
输出
Please enter your name. You will be served in the order of arrival.
name: mm
Please enter your name. You will be served in the order of arrival.
name: ss
Please enter your name. You will be served in the order of arrival.
name: huhu
Please enter your name. You will be served in the order of arrival.
name: lala
Please enter your name. You will be served in the order of arrival.
name: yoyo
The queue is full. Processing begins!
Now processing mm路路路
Now processing ss路路路
Now processing huhu路路路
Now processing lala路路路
Now processing yoyo路路路
要输入英文的省略号点&#xff0c;哈哈哈
Please enter your name. You will be served in the order of arrival.
name: yaya
Please enter your name. You will be served in the order of arrival.
name: huhu
Please enter your name. You will be served in the order of arrival.
name: lala
Please enter your name. You will be served in the order of arrival.
name: hula
Please enter your name. You will be served in the order of arrival.
name: yeye
The queue is full. Processing begins!
Now processing yaya&#96;&#96;&#96;
Now processing huhu&#96;&#96;&#96;
Now processing lala&#96;&#96;&#96;
Now processing hula&#96;&#96;&#96;
Now processing yeye&#96;&#96;
总结
- 总之嵌套类是定义在别的类内部&#xff0c;然后一般只为这个包含类所用
- 嵌套类主要是为了辅助设计包含类&#xff0c;它是为了包含类的实现做贡献的&#xff0c;如果不在public部分声明&#xff0c;则它不就不是公有接口的一部分&#xff0c;只为包含类服务。