C++ 编写自己的异常类

编写自己的异常类有两个好处:
1)C++标准库中的异常数目有限,可以在程序中为特定的错误创建更有意义的类名称,而不是使用具有通常名称的异常类,例如runtime_error
2)可以在异常中加入自己的信息,而标准层次结构中的异常只允许设置错误字符串。例如或许想在异常中传递不同的信息。

示例为文件错误定义自己的错误层次结构,从一个泛型类FileError开始:

#pragma once
#include<iostream>
#include<exception>
#include<string>
using namespace std;
class FileError :public exception
{
public:
	FileError(const string& fileIn) :mFile(fileIn) {};
	virtual const char* what() const noexcept override { return mMgs.c_str(); }
	const string& getFileName() { return mFile; }
protected:
	void setMessage(const string &message) { mMgs = message; }
private:
	string mFile, mMgs;
};

    作为一名优秀程序员,应将FileError作为标准异常层次结构的一部分,将其作为exception的子类是恰当的。当编写exception的派生类时,需要重写what()方法,这个方法的原型已经出现过,其返回值作为一个在对象销毁之前一直有效的const char*字符串。在FileError中,这个字符串来自mMsg数据成员,在构造函数中将其设置为“”。FileError的派生类如果想给出不同的消息,就必须用不同的消息设置这个mMsg字符串。

泛型类FileError还包含文件名、访问文件名的公有方法和受保护的设置方法,以便派生类可以设置该消息。

readIntegerFile()中第一种异常情况是无法打开文件。因此,下面编写FileError的派生类FileOpenError:

#pragma once
#include"FileError.h"
#include<string>
#include<iostream>
using namespace std;
class FileOpenError :public FileError
{
public:
	FileOpenError(const std::string& fileNameIn);
};
FileOpenError::FileOpenError(const std::string & fileNameIn):FileError(fileNameIn)
{
	setMessage("Unable to open" + fileNameIn);
}

       FileOpenError修改mMsg字符串,令其表示文件打开错误。

       readInterFile()的第二种异常情况是无法正确读取文件。对于这一异常,或许应该包含文件中发生错误的行为,以及what()返回的错误信息字符串中的文件名。下面是FileError的派生类FileReadError:

#pragma once
#include<iostream>
#include<string>
#include"FileError.h"
#include<sstream>
using namespace std;
class FileReadError :public FileError
{
public:
	FileReadError(const string&fileNameIn, int lineNumIn);
	int getLineNum() { return mLineNum; }
private:
	int mLineNum;
};

FileReadError::FileReadError(const string & fileNameIn, int lineNumIn):FileError(fileNameIn),mLineNum(lineNumIn)
{
	ostringstream ostr;
	ostr << "Error reading " << fileNameIn << " at line " << lineNumIn;
	setMessage(ostr.str());
}

readIntegerFile()函数

正确设置行号,跟踪所读取的行号

#include"stdafx.h"
#include<iostream>
#include"FileOpenError.h"
#include"FileReadError.h"
#include<fstream>
#include<vector>
using namespace std;
/**
readIntegerFile()函数
正确设置行号,跟踪所读取的行号
*/
void readIntegerFile(const string& fileName, vector<int>&dest)
{
	ifstream istr;
	int temp;
	string line;
	int lineNumber = 0;
	istr.open(fileName);
	if (istr.fail())
	{
		throw FileOpenError(fileName);
	}
	while (!istr.eof())
	{
		//read one line from the file
		getline(istr, line);
		lineNumber++;
		//Create a string stream out of the line.
		istringstream lineStream(line);
		//Read the integers one by one and add them to the vector
		while (lineStream >> temp)
		{
			dest.push_back(temp);
		}
		if (!lineStream.eof())
		{
			//we did not reach the end of the string stream
			//this means that some error occurred while reading this line.
			//throw an exception
			throw FileReadError(fileName, lineNumber);
		}
	}
}
int main()
{
	vector<int> myInts;
	const string fileName = "C:/Users/Administrator/Desktop/IntegerFile.txt";
	try {
		readIntegerFile(fileName, myInts);
	}
	catch (const FileError&e) {
		cerr << e.what() << endl;
		return 1;
	}
	for (const auto element : myInts)
	{
		cout << element << " ";
	}
	cout << endl;
	return 0;
}


txt文件



输出


         编写其对象用作异常的类时,有一个诀窍。当某段代码抛出一个异常时,复制被抛出的值或者对象。也就是说,使用复制构造函数从就对象构造新对象。复制是必须的,因为原始对象在堆栈中的位置较高,可能在异常被捕获之前超出作用域(因此会被销毁,其所占的内存会被回收)。因此,如果编写的类的对象将作为异常抛出,对象必须能复制,这意味着如果动态分配了内存,必须编写析构函数、复制构造函数和赋值运算符。

       注意:作为异常抛出的对象至少按值复制一次。

       异常可能被复制多次,但只有按值(而不是按引用)捕获异常才会如此。

       注意:按引用捕获异常对象可以避免不必要的复制。


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值