C++ Primer Plus 15章 友元、异常

C++ Primer Plus 15章 友元、异常

1.友元

前面介绍过友元成员函数,这里再补充一下友元类。

我们应该先明确一下,友元类的作用是干什么的,前面说继承的时候,继承分为公有继承,私有继承,保护继承。其中公有继承是is-a的关系,剩下两个是has-a的关系。但是总有些分类,它既不属于is-a,也不属于has-a。这个时候就要采用友元类的关系来表示它们。比如电视机和遥控器,他们既有共同的部分,又有相同的部分,我们不能用继承的方法去表示这两个物体,但可以使用友元类去表示它们,更为的合理。

定义声明:

class Tv
{
public:
	friend class Remote;//这里使用friend关键字去声明Remote为友元类
	....
}

class Remote
{
	....
}

但有时候将整个类作为友元又太为臃肿,可能我们只需要其中某个接口或者某个实现方法,此时只需要将其中需要的函数声明为友元函数即可。但是这里将会涉及到一些声明顺序的问题,要尤其小心。

如下:

class  Tv
{
	friend void Remote::set_chan(Tv & t, int c);//要使这句话通过,则必须知道Remote的定义
};
//但是Remote中又有Tv对象,所以此时需要前向声明

class Tv;	//forward declaration
class Remote{...};
class Tv{...};

友元关系还可以分为两种

(1)互为友元

class Tv
{
	friend class Remote;
public:
	void buzz(Remote & r);
	...
};

class Remote
{
friend class Tv;
public:
	void Bool volup(Tv & t){...}
	...
};
inline void Tv::buzz(Remote & r)
{
	...
}

(2)共同友元

class Analyzer;		//forward declaration
class Probe
{
	friend void sync(Analyzer & a, const Probe & p)friend void sync(Probe & p, const Analyzer & a);
}

class Analyzer
{
	friend void sync(Analyzer & a, const Probe & p)friend void sync(Probe & p, const Analyzer & a);
}

2.嵌套类

从字面上很好理解,就是在一个类中,声明了另外一个类。

要注意类嵌套和前面的所述的类包含是不一样的。

包含意味着将类对象作为另一个类的成员;而对类进行嵌套不创建类成员,而是定义了一种类型,该类型仅在包含嵌套类声明的类中有效。

示例:

class Queue
{
	class Node
	{
	public:
		Item item;
		Node * next;
		Node(const Item & i):item(i),next(0){}
	};
	....
}

作用域:

如果嵌套类是在另一个类的私有部分声明的,则只有后者知道它。

如果嵌套类是在另一个类的保护部分声明的,则它对于后者来说是可见的,但对于外部世界则是不可见的。派生类将知道嵌套类,并可以直接创建这种类型的对象。

如果嵌套类是在另一个类的公有部分声明的,则允许后者、后者的派生类以及外部世界使用它。

3.异常

这是C++的特性,有好处,有坏处。

(1)调用abort()

直接异常终止程序

(2)返回错误码

程序如果出错,手动返回错误

(3)异常机制

​ 引发异常

​ 使用处理程序捕获异常

​ 使用try块

示例:

#include<iostream>
double hmean(double a,double b);

int main()
{
	double x,y,z;
	std::cout<<"Enter two numbers: ";
	while(std::cin>>x>>y)
	{
        //从下面开始,都是程序的异常处理部分
        //如果发生了异常,先跳转到throw处,处理完成后,则执行catch模块里面的代码;如果没有,则跳过
        //catch执行完后将返回到while循环的开始位置
		try{
			z = hmean(x,y);
		}
		catch(const char * s)
		{
			std::cout<<s<<std::endl;
			std::cout<<"Enter a new pair of numbers: ";
			continue;
		}
		....
	}
}

double hmean(double a,double b)
{
	if(a == -b)
	{
		throw "bad hmean() arguments:a = -b not allowed";
	}
	return 2.0 * a * b/(a+b);
}

(4)将对象用作异常类型

其实和函数的使用差不多,只是代码的处理更为多一点而已。这里的代码太多,就不展示了。

(5)栈解退

现在假设函数由于出现异常而终止,则程序也将释放栈中的内存,但不会在释放栈的第一个返回地址后停止,而是继续释放栈,知道找到一个位于try块中的返回地址。随后,控制权将转到块尾的异常处理程序,而不是函数调用后面的第一条语句。这个过程被称为栈解退。

(6)exception类

专门用于异常类的头文件

下面是几种不同类型的错误的标准异常处理类型

​ domain_error 值域出问题

​ invalid_argument 不是有效值

​ length_error 没有足够的空间来执行所需的操作

​ out_of_bounds 指示索引错误

​ range_error

​ overflow_error

​ underflow_error

(7)异常迷失方向

第一种情况:由异常规范引发,必须与规范列表中的某种异常匹配,如果不匹配,则发生意外异常。

第二种情况:如果异常不是在函数中引发的,则必须捕获它,如果没被捕获,则成为未捕获异常。

要确保捕获异常,可以做以下措施:

一:确保异常头文件的声明可用

#include

using namespace std;

二:设计一个替代函数,将意外异常转为bad_exception异常

void myUnexpection()

{

​ throw std::bad_expection();

}

三:在程序的开始位置,将意外异常操作指定为调用该函数

set_unexpected(myUnexpected);

四:将bad_exception类型包括在异常规范中,并添加如下catch块序列

double Argh(double, double) throw(out_of_bounds, bad_exception);
...
try{
	x = Argh(a,b);
}
catch(out_of_bounds & ex)
{
	...
}
catch(bad_exception & ex)
{
	...
}


(8)异常的注意事项:动态内存分配导致的异常

示例:

void test2(int n)
{
	double * ar  = new double[n];
	...
	if(oh_no)
		throw exception();
	...
	delete [] ar;
	return;
}

由于栈解退,throw 会调用析构函数,从而释放了栈中的变量ar,导致指针未被正确释放,从而内存泄露。

4.RTTI

给程序在运行阶段确定对象的类型提供一种标准方式。

只适用于包含虚函数的类,因为虚函数可以重定义,从而确定使用同名字,但不用类型的函数。

这就是RTTI的功能。

dynamic_cast运算符

typeid运算符和type_info类

5.类型转换运算符

dynamic_cast运算符

​ 基类向上转换

const_cast运算符

​ 同类型转换

static_cast运算符

​ 隐式转换

reinterpret_cast运算符

​ 危险类型转换

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值