1C++为什么要引入exception?
因为exception无法被忽略。如果一个函数利用“设定状态变量”的方式或是利用“返回错误码”的方式发出一个异常信号,无法保证此函数的调用者会检查那个变量或检验那个错误码(特别是当需要判断返回值的地方特别多的时候,很难每一次调用都去判断一下返回值,并且如果每次调用都去判断返回值的话,会让代码显得啰嗦)。于是程序的执行可能会一直继续下云,远设错误发生地点。但是如果函数以抛出exception的方式发出异常信号,而该exception未被捕捉,程序的执行便会立该中止。
2 利用destructor避免泄露资源
举例:你正在为“小动物收养保护中心”“编写一个软件,收养中心每天都会产生一个文件,其中有它所的当天收养个案。你的工作就是写一个程序,读这些文 件,然后为每一个收养个案做适当处理。合理的想法是定义一个抽象基类ALA(Adorable Little Animal), 再从中派生出针对小狗和小猫的具体类。其中有个虚函数processAdoption, 负责”因动物类而异“的必要处理动作。
你需要一个函数,读取文件内容,并视文件内容产生一个Puppy object或一个Kitten object.
代码如下:
#include <iostream>
#include <string.h>
#include <fstream>
#include <memory>
using namespace std;
class ALA
{
public:
virtual void processAdoption() = 0;
virtual ~ALA() { printf("ALA::%s\r\n",__func__); }
};
class Puppy: public ALA
{
public:
void processAdoption()
{
printf("Puppy::%s\r\n",__func__);
throw invalid_argument("throw execption for testing!");
}
~Puppy() { printf("Puppy::%s\r\n",__func__); }
};
class Kitten: public ALA
{
public:
void processAdoption() { printf("Kitten::%s\r\n",__func__); }
~Kitten() { printf("Kitten::%s\r\n",__func__); }
};
ALA * readALA(istream& dataSource)
{
char name[10] = {0};
dataSource.getline(name, sizeof(name));
if(strcmp(name, "puppy") == 0)
{
printf("new a puppy\r\n");
return new Puppy();
}
printf("new a kitten\r\n");
return new Kitten();
}
void processAdoptions()
{
filebuf buf;
if (buf.open("/root/Coding/more_effective_c++_item9/animal", ios::in) == nullptr)
{
printf("openfile failed\r\n");
return;
}
istream is(&buf);
while(is)
{
ALA *pa = readALA(is);
pa->processAdoption();
delete pa;
}
}
int main()
{
try
{
processAdoptions();
}
catch (invalid_argument e)
{
printf("%s\r\n", e.what());
return -1;
}
return 0;
}
这个文件/root/Coding/more_effective_c++_item9/animal里面的内容如下:
puppy kitten
当Puppy::processAdoption抛出异常时,pa对象没有被delete, 从而造成了资源泄露。那有没有办法在抛出异常时也将资源做正常释放。
方法1:catch?processAdoption exception,在异常发生的时候手动释放资源,但是这样会导致代码比较繁琐,不那么clean.
void processAdoptions()
{
filebuf buf;
if (buf.open("/root/Coding/more_effective_c++_item9/animal", ios::in) == nullptr)
{
printf("openfile failed\r\n");
return;
}
istream is(&buf);
while(is)
{
ALA *pa = readALA(is);
try
{
pa->processAdoption();
}
catch(...)
{
delete pa;
throw;
}
delete pa;
}
}
方法2:将释放资源的操作封装在析构函数里面,这样当局部变量生命周期到时,其会调用析构函数(不管以什么样的方式释放局部变量,正常退出或者抛exception退出)。C++提供的智能指针正是可以用来满足这样的需求。将?processAdoptions稍微改写一下:
void processAdoptions()
{
filebuf buf;
if (buf.open("/root/Coding/more_effective_c++_item9/animal", ios::in) == nullptr)
{
printf("openfile failed\r\n");
return;
}
istream is(&buf);
while(is)
{
unique_ptr<ALA> pa(readALA(is));
pa->processAdoption();
}
}
程序的输出如下:
./a.out
new a puppy Puppy::processAdoption Puppy::~Puppy ALA::~ALA throw execption for testing!
可以看到,当抛出异常时,资源也得到正确释放了,并且代码实现也比较简洁。
|