目录
局部资源管理
- 函数执行之初所分配的资源不一定最终会被释放掉。
比如某函数里使用了new表达式从堆内存里分配空间给一个对象,
然后把对象地址分配给一个指针。
假如这个操作执行完以后,在释放资源的语句之前这个函数或这个函数内调用的函数抛出了异常,
而且没在这个函数里解决,
那么释放资源的语句不会被执行,这就极其危险了(内存泄漏) - 怎么解决呢。需要资源管理的手法(在初始化阶段即进行资源请求)。
- 对于类对象而言,初始化操作发生于类构造函数内,资源的请求也应该在构造函数内完成。资源的释放应该放在析构函数内完成。(不过这样无法将资源管理自动化)
比如:
- 对于类对象而言,初始化操作发生于类构造函数内,资源的请求也应该在构造函数内完成。资源的释放应该放在析构函数内完成。(不过这样无法将资源管理自动化)
#include<memory>
void f()
{
auto_ptr<int>p(new int);//模板类auto_ptr的类对象定义
MutexLock m1(m);//非模板类MutexLock的类对象定义
process(p);
//执行完这些代码,p和m1的析构函数在此被自动调用。(如果前三行代码没有异常抛出的话)
}
- 在异常处理机制终结某个函数的执行之前,C++保证函数中的所有局部对象的析构函数都会被调用。
auto_ptr
- auto_ptr是C++标准库(以前的博客说的标准库都认为是C++标准库)提供的模板类。
- 这个auto_ptr模板类的作用:它会自动删除通过new表达式分配的对象。比如上面例子里的p对象
- 使用auto_ptr模板类之前,要包含头文件
#include<memory>
- auto_ptr模板类将*运算符和->运算符重载,重载方式是用一个迭代器类里声明/定义重载运算符函数(iterator class)的方式来重载。所以我们可以像使用一般指针那样使用auto_ptr对象,如:
auto_ptr<string>aps(new string("vermeer"));
string *ps=new string("vermmer");
if((aps->size()==ps->size())&&(*aps==*ps))//像使用一般指针使用auto_ptr对象
//...
标准异常
- 如果new表达式无法从程序的空闲空间(free store)分配到足够的内存,则抛出bad_alloc异常对象,如:
vector<string>*init_text_vector(ifstream &infile)
{
vector<string>*ptext=0;
try
{
ptext=new vetor<string>;
}
catch(bad_alloc)
{
cerr<<"...";
//处理异常,然后退出catch子句
}
return ptext;
}
解析new表达式的具体工作过程
- 解析一下new表达式的具体工作过程(以
ptext=new vetor<string>;
为例)
①分配足够内存
②将vector<string>
默认构造函数应用于堆对象(new一个对象,你是不知道对象名的)之上
③然后再将对象地址赋值给ptext这个vector<string>
指针- 如果没有足够的内存足以分配给
vector<string>对象
,vector<string>
对象默认构造函数不会被调用,ptext也不会被赋值对象地址。一切这样的现象产生是因为bad_alloc异常对象会被抛出。程序流程调到紧接在try块后的catch子句。
- 如果没有足够的内存足以分配给
bad_alloc异常类
catch(bad_alloc)//这是一个声明
//bad_alloc是个异常类,而非异常对象
并未声明出任何对象。为什么呢,因为我们只对捕获到的异常的类型感兴趣,并没有打算在catch子句中实际操作该对象。
- 如果抑制不让bad_alloc异常被抛出,可以这么写:
//ptext是个vector<string>指针(vector<string>*)
ptext=new (nothrow) vector<string>;
这样如果new分配内存空间失败,会返回0,注意,使用ptext指针之前都应该先检验它是否为0
- C++标准库定义了一套异常类体系。
- 抽象基类是exception异常类,该抽象基类声明了一个what()虚函数,返回const char*,用以表示被抛出异常的文字描述
- bad_alloc异常类派生自exception基类,它有自己的what()成员函数。(VC++提供的bad_alloc异常类的what()函数会产生"bad allocation"这样的信息)
自己编写异常类并将其继承于exception之下
- 可以将自己编写的异常类继承于exception基类之下,注意,代码中提到了exception异常类基类,必须包含头文件
#include<exception>
。
举个例子:
#include<exception>
class iterator_overflow: public exception{
public:
iterator_overflow(int index,int max):_index(index),_max(max){}
int index(){return _index;}
int max(){return _max;}
//接下来的自己编写的异常类iterator_overflow派生类的
//成员函数what()声明覆盖基类exception的成员函数
//what()(exception::what())
const char* what()const;
private:
int _index;
int _max;
- 将自己编写的异常类继承于标准的exception类体系的好处:自己编写的异常类可被任何“打算捕获抽象基类exception”的程序代码所捕获。
这样的程序代码比如:
inline void Triangular_iterator::
check_integrity()
{
if(_index>=Triangular::max_elems)
{
throw iterator_overflow(_index,Triangular::_max_elems);
if(_index>=Triangular::elems.size())
{
Triangular::gen_elemts(_index+1);
}
}
}
这意味着不用修改原有程序代码,就可以让原有程序代码认识自己编写的异常类。也不用catch(…)的形式来捕获所有异常。比如:
catch(const exception &ex)
{
cerr<<ex.what()<<endl;
}
这样的写法,让catch子句捕捉exception基类异常类的所有派生类。当bad_alloc派生类异常类被抛出,这个catch子句里打印“bad allocation”信息。
- 当自己编写的异常类(派生类)被抛出,catch子句里打印自己编写的异常类的覆盖基类(exception类)的what()函数的输出的内容
ostringstream类
- ostringstream class提供“内存内的输出操作”,输出到一个string对象上。
- 作用其类对象是可以将很多不同类型的数据格式化为字符串。(其他类型对象->string型对象)比如将_index,max这类数值对象转换成字符串,让我们省去考虑存储空间和转换算法。
- ostringstream类提供一个成员函数str(),将接收转换成字符串的string对象返回(return)。
- string类的成员函数转换函数c_str()将string型对象转换成const char*型对象,并将转换成的const char*型对象返回(return)。
- 使用ostringstream类之前,必须包含头文件
#include<sstream>
举个代码例子:
#include<sstream>
#include<string>
const char*
iterator_overflow::
what()const
{
ostringstream ex_msg;
static string msg;
//将输出信息写到内存内的ostringstream对象之中
//将整数值转为字符串表示。。。。
ex_msg<<"Internal error:current index"
<<_index<<" exceeds maximum bound: "
<<_max;
msg=ex_msg.str();//萃取转换而来的string对象(字符串)
return msg.c_str();//萃取出const char*类型的字符串
}
istringstream类
- 作用:其类对象可以将实际上应为非字符的数据(整数值、内存地址等)的字符串表示转换为其实际类型(string型对象->其他类型对象)(ostringstream类作用的逆过程)
- 使用istringstream类之前,必须包含头文件
#include<sstream>