【从 C 向 C++ 进阶】- 对 C 的语法拓展 - 异常处理

从 C 向 C++ 进阶系列导航


1. C++ 新增的异常处理

在 C++ 中新增了异常处理语法 try-catch,try 语句处理正常代码逻辑,catch 语句处理异常情况,通过 throw 语句可以抛出异常信息然后由相对应的 catch 语句处理。具体语法如下:

try
{
    ...
}
catch
{
    ...
}

需要注意的是,throw 语句会由于发生地方的不同而表现出不同的异常抛出情形,具体遵循以下规则:

  • 当 throw 语句发生在 try 语句中,所抛出的异常只能命中同作用域中的 catch 语句,同时 catch 语句必须定义在 try 语句之后且两语句之间不能定义任何表达式。
  • 当 throw 语句发生在非 try 语句时,会立即结束当前函数,并带着异常信息返回函数的调用处,由上一级函数处理该抛出的异常。
  • 实验:
void fun()
{
	try
	{
		throw 1;
	}
	catch(...)
	{
		cout << "in fun:1" << endl;
	}
	
	try
	{
		throw 1;
	}
	catch(...)
	{
		cout << "in fun:2" << endl;
		throw 1;
	}
	
	try
	{
		throw 1;
	}
	catch(...)
	{
		cout << "in fun:3" << endl;
		throw 1;
	}
}

void tmp()
{
	fun();
	cout << "in tmp()" << endl;
}

int main(void)
{
	try
	{
		tmp();
	}
	catch(...)
	{
		cout << "in main" << endl;
	}
}

以上运行后打印:

in fun:1
in fun:2
in main

可以看到,程序的执行流为 main() => tmp() => fun(),在 fun() 的第一个 try 语句中抛出了异常并被紧接下来的 catch() 语句捕获,但 catch 语句仅进行了信息打印,因此程序继续往下执行。在第二个异常被接下来的 catch() 捕获后,随后抛出异常,此时退出 fun() 函数,异常信息被传递到 tmp(),由于 tmp() 中的 fun() 的调用在非 try 语句中,此时马上结束 tmp() 函数,异常传递到 main() 中,由于在 main() 中 tmp() 是在 try 语句中调用 ,因此该异常被 catch 语句所捕获。

2. catch 捕获规则

一个 try 语句可以对应多个 catch 语句。catch 语句可以定义具体处理的异常类型,不同类型的异常由不同的 catch 语句负责处理。try 语句中可以抛出任何类型的异常,catch(...) 用于捕获所有类型的异常,任何异常都只能被其中一个 catch 捕获。

当 try 中的 throw 抛出异常后,会自上而下按照数据类型严格匹配每个 catch 的处理类型,且匹配过程中不能进行任何的类型转换。catch 可以定义形参来接收异常信息,也可以只指定捕获的目标数据类型。

  • 实验:
template <typename T>
void fun(T var)
{
	try
	{
		throw var;
	}
	catch(int)
	{
		cout << "in catch(int)" << endl;
	}
	catch(char)
	{
		cout << "in catch(char)" << endl;
	}
	catch(float)
	{
		cout << "in catch(float)" << endl;
	}
	catch(string str)
	{
		cout << "\"" << str << "\"" << " is string" << endl;
	}
	catch(...)
	{
		cout << "type is others" << endl;
	}
}

int main(void)
{
	fun<int>(1);			// in catch(int)
	fun<char>('a');			// in catch(char)
	fun<string>("123");		// "123" is string
	fun<float>(3.14);		// in catch(float)
	fun<double>(3.14);		// type is others
}

需要注意的是,当 catch 捕获的是自定义类类型时,必须保证子类类型在前,父类类型在后,否则当父类类型在前时会兼容子类类型,导致异常被不正确的 catch 所捕获。

3. 函数的异常声明

在函数的声明和定义时可以直接指定可能抛出的异常类型,函数的异常类型声明可以提高代码可读性,且遵循以下规则:

  • 函数异常声明是一种与编译器之间的契约;
  • 函数声明异常后就只能抛出声明的异常,抛出其他异常将导致程序运行终止。
  • 可以直接通过异常声明定义无异常函数。

无异常函数具体形式如下:

void fun() try
{
    ...
}
catch(...)
{
    ...
}

通过 try 语句与 castch 语句把函数体分为两部分,try 语句中为正常的程序语句,catch 语句中为异常情况发生的处理函数。

  • 实验:
int func(int i, int j) throw(int)
{
    if( (0 < j) && (j < 10) )
    {
        return (i + j);
    }
    else
    {
        throw '0';
    }
}

void test(int i) try
{
    cout << "func(i, i) = " << func(i, i) << endl;
}
catch(int j)
{
    cout << "Exception: " << j << endl;
}
catch(...)
{
    cout << "Exception..." << endl;
}

int main()
{
    test(5);   
    test(10);
    
    return 0;
}

以上程序编译后运行结果为:

func(i, i) = 10
terminate called after throwing an instance of 'char'
/usr/bin/Run: line 2:   909 Aborted                 (core dumped) ./output/app

可以看到,当传参为 10 时 func 会抛出 char 类型的异常,但由于已声明只能抛出 int 类型的异常,因此程序会被异常中止。

特殊的 ,当函数声明异常为 throw() 即类型为空时,表示当前函数将不能抛出任何异常。

发布了60 篇原创文章 · 获赞 36 · 访问量 5924

猜你喜欢

转载自blog.csdn.net/qq_35692077/article/details/102761560