首页 > 编程语言 >c++学习笔记——模板和IO(二)

c++学习笔记——模板和IO(二)

时间:2023-02-23 12:23:10浏览次数:40  
标签:func 函数 int void c++ IO 异常 模板 cout

C++异常

前言:

异常处理就是处理程序中的错误。所谓错误是指在程序运行的过程中发生的一些异常事件(如:除0溢出,数组下标越界,所要读取的文件不存在,空指针,内存不足等等)
在对C语言的学习中,我们常常对错误的处理围绕着两种方法:一种是使用整型的返回值标识错误;二是使用errno宏(可以简单的理解为一个全局整型变量)去记录错误。
但这两种方法最大的缺陷就是会出现不一致问题。例如有些函数返回1表示成功,返回0标识错误;而有些函数返回0标识成功,返回非0表示错误。还有一个缺陷是函数的返回值只有一个,你通过函数的返回值表示错误代码,那么函数就不能返回其他的值。

C++相比C语言在异常处理上有哪些优势呢?

我们可先看看下面异常语法的代码再进行总结。

//异常的基本语法
int func(int a, int b)
{
	if (b == 0)
	{
		//第二步:
		throw 10;	//抛出一个int类型的异常
	}

	return a / b;
}

void test()
{
	int a = 10;
	int b = 0;
	//把有可能出现异常的代码块放到try中
	try
	{
		func(a, b);  //第一步
	}
	catch (int)	//第三步
	{
		cout << "接收一个int类型的异常" << endl;
	}
}

回到上面的问题

  1. 函数的返回值可以忽略,但异常不可忽略。如果程序出现异常,但是没有被捕获,程序就会终止,这多少会促使程序员开发出来的程序更健壮一点。而如果使用C语言的error宏或者函数返回值,调用者都有可能忘记检查,从而没有对错误进行处理,结果造成程序莫名其面的终止或出现错误的结果。
  2. 整型返回值没有任何语义信息。而异常却包含语义信息,有时你从类名就能够体现出来。
  3. 整型返回值缺乏相关的上下文信息。异常作为一个类,可以拥有自己的成员,这些成员就可以传递足够的信息。
  4. 异常处理可以在调用跳级。这是一个代码编写时的问题:假设在有多个函数的调用栈中出现了某个错误,使用整型返回码要求你在每一级函数中都要进行处理。而使用异常处理的栈展开机制,只需要在一处进行处理就可以了,不需要每级函数都处理。

异常具有严格的类型匹配

异常机制和函数机制互不干涉,但是捕捉方式是通过严格类型匹配。

int func(int a, int b)
{
	if (b == 0)
	{
		throw 20.2f;
	}
	return a / b;
}
void test()
{
	int a = 10;
	int b = 0;

	try
	{
		func(a, b);
	}
	catch (double s)
	{
		cout << "接收一个double类型的异常" << endl;
	}
	catch (char)
	{
		cout << "接收一个char类型的异常" << endl;
	}
	catch (...)	//接收其他类型的异常
	{
		cout << "接收一个其他类型的异常" << endl;
	}
}

栈解旋(unwinding)

异常被抛出后,从进入try块起,到异常被抛掷前,这期间在栈上构造的所有对象,都会被自动析构。析构的顺序与构造的顺序相反,这一过程称为栈的解旋(unwinding).

class Maker
{
public:
	Maker()
	{
		cout << "Maker的构造" << endl;
	}
	Maker(const Maker& m)
	{
		cout << "Maker的拷贝构造" << endl;
	}
	~Maker()
	{
		cout << "Maker的析构" << endl;
	}
};
void func()
{
	//在抛出异常的函数中,如果抛出异常之后,但函数没有结束,这时,栈上申请的对象都会被释放
	//这就叫栈解旋
	Maker m;
	throw m;//这个m是Maker m拷贝构造的

	cout << "func函数结束" << endl;
}

void test()
{
	try
	{
		func();
		cout << "func()代码后" << endl;
	}
	catch (Maker)
	{
		cout << "接收一个Maker类型的异常" << endl;
	}

}

运行结果:

异常接口声明

  1. 为了加强程序的可读性,可以在函数声明中列出可能抛出异常的所有类型,例如:void func() throw(A,B,C);这个函数func能够且只能抛出类型A,B,C及其子类型的异常。
  2. 如果在函数声明中没有包含异常接口声明,则此函数可以抛任何类型的异常,例如:void func()
  3. 一个不抛任何类型异常的函数可声明为:void func() throw()
  4. 如果一个函数抛出了它的异常接口声明所不允许抛出的异常,unexcepted函数会被调用,该函数默认行为调用terminate函数中断程序。
void func() throw(int, char)	//只允许抛出 int 或者 char异常
{
	throw 10;	//抛出一个double类型的异常
}

void test()
{
    try
    {
        func();
    }
    catch (int)
    {

        cout << "抛出一个int型的异常";
    }
    catch (...)
    {
        cout << "抛出一个其他型的异常";
    }
}

运行结果:

异常变量周期

产生三个对象

class Maker
{
public:
	Maker()
	{
		cout << "Maker的构造" << endl;
	}
	Maker(const Maker& m)
	{
		cout << "Maker的拷贝构造" << endl;
	}
	~Maker()
	{
		cout << "Maker的析构" << endl;
	}
};
//产生三个对象
void func1()
{
	Maker m;//第一个对象,在异常接收前被释放
	throw m;//第二个对象,是第一个对象拷贝过来的
}
void test01()
{
	try
	{
		func1();
	}
	catch (Maker m1)//第三个对象,是第二个对象拷贝过来的
	{
		cout << "接收一个Maker类型的异常" << endl;
		//第二个和第三个对象在catch结束时释放

	}
}

产生两个对象

void func2()
{
	//第一个对象
	throw Maker();//匿名对象
}

void test02()
{
	try
	{
		func2();
	}
	catch (Maker m1)//第二个对象
	{
		cout << "接收一个Maker类型的异常" << endl;
		//第一个和第二个对象在catch结束时释放

	}
}

产生一个对象

void func3()  //常用这种方式
{
	throw Maker();//匿名对象

}

void test03()
{
	try
	{
		func3();
	}
	catch (Maker& m1)
	{
		cout << "接收一个Maker类型的异常" << endl;
	}
}

异常的多态使用

//异常的基类
class Father
{
public:
	virtual void printM()
	{

	}
};
//1、有继承
class SonNULL :public Father
{
public:
	virtual void printM()	//2、重写父类的虚函数
	{
		cout << "空指针的异常" << endl;
	}
};

class SonOut :public Father
{
public:
	virtual void printM()
	{
		cout << "越位溢出" << endl;
	}
};

void func(int a,int b)
{
	if (a == 0)
	{
		throw SonNULL();
	}
	if (b == 0)
	{
		throw SonOut();
	}
}

void test()
{
	int a = 0;
	int b = 10;
	try
	{
		func(a, b);
	}
	catch (Father& f)	//3、父类引用指向子类对象
	{
		f.printM();
	}
}

标签:func,函数,int,void,c++,IO,异常,模板,cout
From: https://www.cnblogs.com/cloudseastore/p/17147485.html

相关文章

  • C/C++数据结构与算法课程设计选题详情[2023-02-23]
    C/C++数据结构与算法课程设计选题详情[2023-02-23]选题详情选题一:迷宫与栈问题【问题描述】以一个mXn的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,......
  • cookie,session,token,drf-jwt
    1.cookie,session,token发展史引入:我们都知道http协议本身是一种无状态的协议,一个普通的http请求简单分为三步:客户端发送请求request服务端收到请求并进行处理服务端将结......
  • C++常用语法积累
    判断素数#include<iostream>#include<cmath>usingnamespacestd;boolisPrime(intn){for(inti=2;i<=sqrt(n);i++){if(n%i==0){......
  • Linux安装Taiyi stable-diffusion-webui
    1.安装环境操作系统及版本:Ubuntu20.04.5GPU:8GBGPU驱动(我是阿里云GPU服务器) Linux手动安装GPU驱动参考:https://docs.nvidia.com/datacenter/tesla/tesla-inst......
  • 【C#进阶】.NET Core 中的筛选器 Filter【ActionFilterAttribute 操作筛选器,实现日志
     筛选器Filter介绍:【C#进阶】.NETCore中的筛选器Filter-C#初级程序员-博客园(cnblogs.com) ActionFilterAttribute操作筛选器,实现日志记录第一步创建.NE......
  • C/C++程序隐藏符号
    使用visibility#ifdef__cplusplus//如果是C++语言#definePASSPORT_EXTERNextern"C"__attribute__((visibility("default")))#else#definePASSPORT_EX......
  • 异步重叠IO
    typedefstruct_OVERLAPPED{DWORDInternal;DWORDInternalHigh;DWORDOffset;DWORDOffsetHigh;HANDLEhEvent;}OVERLAPPED;这个结构体是重......
  • Content-Disposition
    Content-Disposition文件下载响应头的设置content-type 指示响应内容的格式content-disposition 指示如何处理响应内容。一般有两种方式:inline:直接在页面显示attc......
  • 大规模 IoT 边缘容器集群管理的几种架构-4-Kubeedge
    前文回顾大规模IoT边缘容器集群管理的几种架构-0-边缘容器及架构简介大规模IoT边缘容器集群管理的几种架构-1-Rancher+K3s大规模IoT边缘容器集群管理的几种架构-......
  • C++ 11
    C++11有哪些新特性?nullptr替代NULL引入了auto和decltype这两个关键字实现了类型推导基于范围的for循环for(auto&i:res){}类和结构体的中初始化列表Lambda......