【友元、异常和其他】——C++ Prime Plus CH15

本文详细探讨了C++中的友元、嵌套类、异常处理和运行时类型信息(RTTI)的概念。在友元部分,解释了友元类和成员函数的用法以及友元关系。嵌套类阐述了嵌套类的作用域、访问权限和模板中的应用。异常处理部分介绍了异常的基本机制、错误码返回、栈解退和异常规范。RTTI部分讨论了dynamic_cast、typeid和type_info类的使用,以及类型转换运算符如const_cast、static_cast和reinterpret_cast的功能和应用场景。
摘要由CSDN通过智能技术生成

①友元

1.介绍

前边的章节中,我们将友元函数用作类的扩展接口中,我们也可以将类作为友元。在这种情况下,友元类的所有方法都可以访问原始类的私有成员和保护成员。也可以做更严格的定义,只让特定的成员函数指定为另一个类的友元。哪些函数、成员函数或类为友元是由类定义的。

2.友元类

什么时候希望一个类成为另一个类的友元呢?当二者可以相互影响或单方面影响的时候。例如,电视机和遥控(既不是Is-a,也不是has-a),而是遥控器可以影响电视(换台,调音等)。我们来实现这个例子。

#ifndef TV_H_
#define TV_H_

class Tv
{
private:
	int state;     //on or off
	int volume;
	int maxchannel;
	int channel;    //current channel seeting
	int mode;      //broadcast or cable
	int input;   //TV or DVD
public:
	enum { Off, On };
	enum { Minval, Maxval = 20 };
	enum { Antenna, Cable };
	enum { TV, DvD };
	Tv(int s = Off, int mc = 125) :state(s), maxchannel(mc), volume(5), channel(2), mode(Cable), input(TV) {}
	void onoff() { state = (state == Off) ? On : Off; }  //切换状态
	bool ison() { return state == On; }

	bool volup();
	bool voldown();

	void chanup();
	void chandown();

	void set_mode() { mode = (mode == Antenna) ? Cable : Antenna; }
	void set_input() { input = (input == TV) ? DvD : TV; }

	void settings() const;

	friend class Remote;

};
class Remote
{
private:
	int mode;
public:
	Remote(int m = Tv::TV):mode(m){}

	bool volup(Tv& t) { return t.volup(); }
	bool voldown(Tv& t) { return t.voldown(); }
	void onoff(Tv& t) { t.onoff(); }
	void channup(Tv& t) { t.chanup(); }
	void chandown(Tv& t) { t.chandown(); }
	void set_chan(Tv& t, int c) { t.channel = c; }
	void set_mode(Tv& t, int c) { t.set_mode(); }
	void set_input(Tv& t, int c) { t.set_input(); }

};

#endif 

友元类声明: 

friend class Remote;

#include<iostream>
#include"TV.h"

bool Tv::volup()
{
	if (volume < Maxval)
	{
		volume++;
		return true;
	}
	else
		return false;
}
bool Tv::voldown()
{
	if (volume < Minval)
		return false;
	volume--;
	return true;
}

void Tv::chanup()
{
	if (channel < maxchannel)
	{
		channel++;
	}
	else
		channel = 1;
}
void Tv::chandown()
{
	if (channel > 1)
	{
		channel--;
	}
	else
		channel = maxchannel;
}

void Tv::settings() const
{
	std::cout << "TV is " << (state == Off ? "Off" : "On") << std::endl;
	if (state == On)
	{
		std::cout << "Volume setting = " << volume << std::endl;
		std::cout << "Channel setting = " << channel << std::endl;
		std::cout << "Mode = " << (mode == Antenna ? "Antenna" : "cabel") << std::endl;
		std::cout << "input = " << (input == TV ? "TV" : "DvD") << std::endl;

	}
}
#include<iostream>
#include"TV.h"

int main()
{
	using std::cout; 
	Tv s42;
	cout << "Initial settings for 42\"TV\"\n";
	s42.settings();
	s42.onoff();
	s42.chanup();
	cout<<"\nAdjusted settings for 42\"TV\"\n";
	s42.settings();

	Remote grey;

	grey.set_chan(s42, 10);
	grey.volup(s42);
	grey.volup(s42);
	cout << "\n Settings after using remote:\n";
	s42.settings();

	Tv s58(Tv::On);
	s58.set_mode();
	grey.set_chan(s58, 28);
	cout << "s58 setting:\n";
	s58.settings();
	return 0;

}

 说明:练习的主要目的表明,友元类是一种自然语言,用于表示一些关系。如果不这样,则必须将Tv类私有部分设置为公有,或建立包含两个声明的大型类,着都无法反应这样一个事实:同一个遥控器可用于多台电视机。

3.友元成员函数

上一个例子中,大多数Remote方法都是通过Tv类的公有接口实现的。唯一直接访问Tv成员的Remote方法是set::chan();因此我们可以将这个类的成员函数作为Tv类的友元函数。这样做的麻烦在于声明和定义的顺序。

1.在Tv类中将该成员函数声明为友元。编译器要处理,必须知道Remote定义,Remote在Tv前,但是Remote方法的参数又有Tv类,这意味着Tv类在前。解决该问题的方法是:使用前向说明,并且先声明,最后定义。

class Tv;

class Remote{...}

class Tv{...}     

我们看到 remote()函数定义出现了调用Tv方法,Tv还没定义 ,所以解决办法就是将remote定义放在Tv后。(友元类不需要前向声明,因为 友元语句本身指出它就是个类)

#ifndef TV_H_
#define TV_H_

class Tv;
class Remote
{
private:
	int mode;
public:
	Remote(int m = Tv::TV) :mode(m) {}

	bool volup(Tv& t);
	bool voldown(Tv& t);
	void onoff(Tv& t);
	void channup(Tv& t);
	void chandown(Tv& t);
	void set_chan(Tv& t, int c); 
	void set_mode(Tv& t, int c); 
	void set_input(Tv& t, int c); 

};
class Tv
{
private:
	int state;     //on or off
	int volume;
	int maxchannel;
	int channel;    //current channel seeting
	int mode;      //broadcast or cable
	int input;   //TV or DVD
public:
	enum { Off, On };
	enum { Minval, Maxval = 20 };
	enum { Antenna, Cable };
	enum { TV, DvD };
	Tv(int s = Off, int mc = 125) :state(s), maxchannel(mc), volume(5), channel(2), mode(Cable), input(TV) {}
	void onoff() { state = (state == Off) ? On : Off; }  //切换状态
	bool ison() { return state == On; }

	bool volup();
	bool voldown();

	void chanup();
	void chandown();

	void set_mode() { mode = (mode == Antenna) ? Cable : Antenna; }
	void set_input() { input = (input == TV) ? DvD : TV; }

	void settings() const;

	friend void Remote::set_chan(Tv& t, int c); ;

};

bool Remote::volup(Tv& t) { return t.volup(); }
bool Remote::voldown(Tv& t) { return t.voldown(); }
void Remote::onoff(Tv& t) { t.onoff(); }
void Remote::channup(Tv& t) { t.chanup(); }
void Remote::chandown(Tv& t) { t.chandown(); }
void Remote::set_chan(Tv& t, int c) { t.channel = c; }
void Remote::set_mode(Tv& t, int c) { t.set_mode(); }
void Remote::set_input(Tv& t, int c) { t.set_input(); }
#endif 

4.其他友元关系

上边只介绍了遥控器影响电视状态,单方面影响,如果两个类互相影响,则互为对方的友元函数。在具体调用方法和顺序前,一定要记得先声明,后定义。

5.共同的友元

函数需要访问两个类的私有数据。从逻辑上看,这样的函数应该是每个类的成员函数,但这是不可能的。它可以是一个类的成员,同时是另一个类的友元,但有时将函数作为两个类的友元更合理。一个函数分别在一个类中声明两次。

class Analyzer;
class Probe
{
	friend void sync(Analyzer &a, const Probe &p);   // sync a to p
	friend void sync(Probe &p, cosnt Analyzer &a);   // sync p to a
}
 
class Analyzer
{
	friend void sync(Analyzer &a, const Probe &p);  // sync a to p
	friend void sync(Probe &p, cosnt Analyzer &a);  // sync p to a
}
 
inline void sync(Analyzer &a, const Probe &p)
{
	...
}
inline void sync(Probe &p, cosnt Analyzer &a)
{
 
}

②嵌套类

1.嵌套类

在C++中,可以将类声明放在另一个类中,在另一个类中被声明的类称为嵌套类。包含类的成员函数可以创建和使用被嵌套类的对象,仅当声明位于公有部分,才能在包含类的外边使用嵌套类,而且必须使用作用域解析运算符。

对类进行嵌套与包含不同,包含是将类对象作为另一个类的成员,而对类进行嵌套不创建成员,而是定义了一种类型,为了更好的实现包含类。

对于队列的实现来说,我们包含了结构体Node。在队列中,只有enqueue()方法创建了Node对象,我们可以将创建新对象方法交给类的构造函数来完成。

class Queue
{
private:
	// 这里Node是一个嵌套的结构体定义
	// Item是一个别名, 详见之前的笔记
	struct Node {Item item; struct Node * next;}
	...
};
bool Queue::enqueue(const Item & item)
{
	if(isfull())
		return false;
	Node * add = new Node;
	add->item = item;
	add->next = NULL:
	...
}

采用嵌套类的构造函数来完成:

class Queue
{
	// 嵌套类
	class Node
	{
	public:            //全是公有
		Item item;
		Node * next;
		// 构造函数, 将item赋值为i, next指针设置为0, 也就是空值指针
		Node(const Item & i): item(i), next(0) {}
	};
	...
};

bool Queue::enqueue(const Item & item)
{
	if(isfull())
		return false;
	Node * add = new Node(item);
	...
}

 这个例子中是在类声明中定义了构造函数, 假设想在方法文件中定义构造函数, 则定义必须指出Node类是在Queue类中定义的, 我们可以通过两次作用域解析运算符来完成:

Queue::Node::Node(const Item & i): item(i), next(0) {}

2.嵌套类和访问权限

嵌套类的声明位置决定了嵌套类的作用域, 即它决定了程序的那些部分可以创建这种类的对象.

其次.嵌套类的公部分, 保护部分和私有部分控制了对类成员的访问。(哪些部分可以访问这些数据)

1.作用域


如果嵌套类是在另一个类的私有部分声明的, 则只有包含类知道它,派生类和外部均不知道嵌套类的存在,更别说使用。( 类的默认访问权限是private) 

如果嵌套类是在另一个类的保护部分声明的, 则它对于后者和基于后者的派生类可见(可直接创建对象), 但是对于外部世界是不可见的。

如果嵌套类是在另一个类的共有部分声明的, 则允许后者, 后者的派生类以及外部世界使用它,然而, 由于嵌套类的作用域为包含它的类, 因此在外部世界使用它时, 必须使用类限定符.
2.访问控制


对嵌套类的访问控制规则与常规类相同. (私有不可见,只有公有部分可见,保护部分派生可见,外部不可见。这也是为什么Node均声明为公有的)

类声明的位置决定了类的作用域或可见性, 类可见后, 访问控制规则(共有, 保护, 私有, 友元)将决定程序对嵌套类成员的访问权限.

3.模板中嵌套类

// queuetp.h
#ifndef QUEUETP_H_
#define QUEUETP_H_

// 模板类
template <class T>
class QueueTP
{
private:
	enum { Q_SIZE = 10 };
	// 定义一个嵌套类
	class Node
	{
	public:
		T item;    //用到了模板
		Node* next;
		Node(const T& i) :item(i), next(0) {}
	};
	Node* front;
	Node* rear;
	int items;
	const int qsize;
	QueueTP(const QueueTP& q) : qsize(0) {}
	QueueTP& operator=(const QueueTP& q) { return *this; }

public:
	QueueTP(int qs = Q_SIZE);
	~QueueTP();
	bool isempty() const
	{
		return items == 0;
	}
	bool isfull() const
	{
		return items == qsize;
	}
	bool queuecount() const
	{
		return items;
	}
	bool enqueue(const T& item);
	bool dequeue(T& item);
};

template<class T>
QueueTP<T>::QueueTP(int qs) :qsize(qs)
{
	// 置为空值指针
	front = rear = 0;
	items = 0;
}

template<class T>
QueueTP<T>::~QueueTP()
{
	Node* temp;
	while (front != 0)
	{
		temp = front;
		front = front->next;
		delete temp;
	}
}

template <class T>
bool QueueTP<T>::enqueue(const T& item)
{
	if (isfull())
		return false;
	Node* add = 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值