改善C++ 程序的150个建议学习之建议31:了解new_handler的所作所为
2013-09-16 08:58
465 查看
建议31:了解new_handler的所作所为
在使用operator new申请内存失败后,编译器并不是不做任何的努力直接抛出std::alloc异常,在这之前,它会调用一个错误处理函数(这个函数被称为new-handler),进行相应的处理。
通常,一个好的new-handler函数的处理方式必须遵循以下策略之一:Make more memory available(使更大块内存有效)operator new会进行多次的内存分配尝试,这可能会使其下一次的内存分配尝试成功。其中的一个实现方法是在程序启动时分配一大块内存,然后在new-handler第一次被调用时释放它供程序使用。Install a different new handler(装载另外的new-handler)程序中可以同时存在多个new-handler,假如当前的new-handler不能获得更多的内存供operator
new分配使用,但另一个new-handler却可以做到。在这种情形下,当前的new-handler则会通过调用set_new_handler在它自己的位置上安装另一个new-handler。当operator new 下一次调用 new-handler时,它会调用最新安装的那一个。Deinstall the new-handler(卸载new-handler)换句话说,就是将空指针传给set_new_handler,此时就没有了相应的new-handler。当内存分配失败时,operator new则会抛出一个异常。Throw
an exception(抛出异常)抛出一个类型为bad_alloc或继承自bad_alloc的其他类型的异常。Not return(无返回)直接调用abort或exit结束应用程序。以上的这些处理方式让我们在实现new-handler functions时拥有了更多的选择与自由。这些各式各样的new-handler函数是可以通过调用标准库函数set_new_handler进行特殊定制的,你可以按照自己的方式来对编译器的这一行为进行设定。这个函数同样也声明在<new> 中:
namespace std
{
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
通过函数声明可以看到set_new_handler的形参是一个指向函数的指针,这个函数在operator new无法分配被请求的内存时调用。set_new_handler的返回值是一个指向函数的指针,指向的是set_new_handler调用之前的异常处理函数。所以,可以按照以下方式使用set_new_handler函数:
//error-handling function
void MemErrorHandling()
{
std::cerr << "Failed to allocate memory\n";
std::abort();
}
//Application
const long long DATA_SIZE = 1024*1024*1024;
int main()
{
std::set_new_handler(MemErrorHandling);
std::cout << "Attempting to allocate 1 GB...";
char *pDataBlock = NULL;
try
{
pDataBlock = new char[DATA_SIZE];
}
catch(std::alloc& e)
{
... //some processing codes
}
... // other processing code
}
假如operator new分配空间的请求得不到满足,MemErrorHandling函数将被调用,程序将按照函数中的设定处理方式运行。在标准C++中,标准set_new_handler为用户类统一指定了错误处理函数global new-handler。上述代码采用的就是global newhandler形式。通过上述示例代码可以看出,new_handler必须有主动退出的功能,否则就会导致
operator new内部死循环。因此new_handler一般会采用如下形式,伪代码表示如下:
void MemErrorHandling()
{
if( 有可能使得operator new成功)
{
做有可能使得operator new成功的事
return;
}
// 主动退出
abort/exit 直接退出程序
或set_new_handler(其他newhandler);
或set_new_handler(0)
或throw bad_alloc()或派生类
}
当然,我们可以根据被分配对象的不同,采用不同的方法对内存分配失败进行处理,实现对class-specific new-handlers 的支持。为了实现这一行为,需要为每一个class 提供专属的set_new_handler和operator new版本。假设要为A class设定特殊的内存分配失败处理方式,则需要在类A中声明一个new_handler类型的静态成员(statimember),并将其设置为A class的new-handler处理函数。所以就得到了下面的代码:
class A
{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void * operator new(std::size_t size) throw(std::bad_alloc);
static void MemoryErrorHandling();
private:
static std::new_handler m_curHandler;
};
// 静态类成员定义
std::new_handler A::m_curHandler = NULL;
C++标准中规定set_new_handler函数应该保存传递给它的函数指针,并返回前次调用时被保存的函数指针。上面A类中的set_new_handler也应该这么做:
std::new_handler A::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = m_curHandler;
m_curHandler = p;
return oldHandler;
}
接下来,我们自己定义该类的处理函数:
void MemoryErrorHandling()
{
... // processing code
}
void * operator new(std::size_t size) throw(std::bad_alloc)
{
set_new_handler(MemoryErrorHandling);
return ::operator new(size);
}
当然我们可以采用更好的设计方式(如类继承)来实现,这里就不赘述。如果读者感兴趣可以自己思考一下,或者求助于资源丰富的Internet,它会提供详尽的参考资料。
请记住:了解new_handler的所作所为,并通过标准库函数set_new_handler对内存分配请求不能被满足的处理函数进行特殊定制。
在使用operator new申请内存失败后,编译器并不是不做任何的努力直接抛出std::alloc异常,在这之前,它会调用一个错误处理函数(这个函数被称为new-handler),进行相应的处理。
通常,一个好的new-handler函数的处理方式必须遵循以下策略之一:Make more memory available(使更大块内存有效)operator new会进行多次的内存分配尝试,这可能会使其下一次的内存分配尝试成功。其中的一个实现方法是在程序启动时分配一大块内存,然后在new-handler第一次被调用时释放它供程序使用。Install a different new handler(装载另外的new-handler)程序中可以同时存在多个new-handler,假如当前的new-handler不能获得更多的内存供operator
new分配使用,但另一个new-handler却可以做到。在这种情形下,当前的new-handler则会通过调用set_new_handler在它自己的位置上安装另一个new-handler。当operator new 下一次调用 new-handler时,它会调用最新安装的那一个。Deinstall the new-handler(卸载new-handler)换句话说,就是将空指针传给set_new_handler,此时就没有了相应的new-handler。当内存分配失败时,operator new则会抛出一个异常。Throw
an exception(抛出异常)抛出一个类型为bad_alloc或继承自bad_alloc的其他类型的异常。Not return(无返回)直接调用abort或exit结束应用程序。以上的这些处理方式让我们在实现new-handler functions时拥有了更多的选择与自由。这些各式各样的new-handler函数是可以通过调用标准库函数set_new_handler进行特殊定制的,你可以按照自己的方式来对编译器的这一行为进行设定。这个函数同样也声明在<new> 中:
namespace std
{
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
}
通过函数声明可以看到set_new_handler的形参是一个指向函数的指针,这个函数在operator new无法分配被请求的内存时调用。set_new_handler的返回值是一个指向函数的指针,指向的是set_new_handler调用之前的异常处理函数。所以,可以按照以下方式使用set_new_handler函数:
//error-handling function
void MemErrorHandling()
{
std::cerr << "Failed to allocate memory\n";
std::abort();
}
//Application
const long long DATA_SIZE = 1024*1024*1024;
int main()
{
std::set_new_handler(MemErrorHandling);
std::cout << "Attempting to allocate 1 GB...";
char *pDataBlock = NULL;
try
{
pDataBlock = new char[DATA_SIZE];
}
catch(std::alloc& e)
{
... //some processing codes
}
... // other processing code
}
假如operator new分配空间的请求得不到满足,MemErrorHandling函数将被调用,程序将按照函数中的设定处理方式运行。在标准C++中,标准set_new_handler为用户类统一指定了错误处理函数global new-handler。上述代码采用的就是global newhandler形式。通过上述示例代码可以看出,new_handler必须有主动退出的功能,否则就会导致
operator new内部死循环。因此new_handler一般会采用如下形式,伪代码表示如下:
void MemErrorHandling()
{
if( 有可能使得operator new成功)
{
做有可能使得operator new成功的事
return;
}
// 主动退出
abort/exit 直接退出程序
或set_new_handler(其他newhandler);
或set_new_handler(0)
或throw bad_alloc()或派生类
}
当然,我们可以根据被分配对象的不同,采用不同的方法对内存分配失败进行处理,实现对class-specific new-handlers 的支持。为了实现这一行为,需要为每一个class 提供专属的set_new_handler和operator new版本。假设要为A class设定特殊的内存分配失败处理方式,则需要在类A中声明一个new_handler类型的静态成员(statimember),并将其设置为A class的new-handler处理函数。所以就得到了下面的代码:
class A
{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void * operator new(std::size_t size) throw(std::bad_alloc);
static void MemoryErrorHandling();
private:
static std::new_handler m_curHandler;
};
// 静态类成员定义
std::new_handler A::m_curHandler = NULL;
C++标准中规定set_new_handler函数应该保存传递给它的函数指针,并返回前次调用时被保存的函数指针。上面A类中的set_new_handler也应该这么做:
std::new_handler A::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = m_curHandler;
m_curHandler = p;
return oldHandler;
}
接下来,我们自己定义该类的处理函数:
void MemoryErrorHandling()
{
... // processing code
}
void * operator new(std::size_t size) throw(std::bad_alloc)
{
set_new_handler(MemoryErrorHandling);
return ::operator new(size);
}
当然我们可以采用更好的设计方式(如类继承)来实现,这里就不赘述。如果读者感兴趣可以自己思考一下,或者求助于资源丰富的Internet,它会提供详尽的参考资料。
请记住:了解new_handler的所作所为,并通过标准库函数set_new_handler对内存分配请求不能被满足的处理函数进行特殊定制。
相关文章推荐
- 改善C++ 程序的150个建议学习之建议28:new/delete与new[]/delete[]必须配对使用
- 改善C++ 程序的150个建议学习之建议29:区分new的三种形态
- 改善C++ 程序的150个建议学习之建议30:new内存失败后的正确处理
- 改善C++ 程序的150个建议学习之建议33:小心翼翼地重载operator new/ operator delete
- 改善C++ 程序的150个建议学习之建议34:用智能指针管理通过new创建的对象
- 改善C++ 程序的150个建议学习之建议5:不要忘记指针变量的初始化
- 改善C++ 程序的150个建议学习之建议16:慎用goto
- 改善C++ 程序的150个建议学习之建议17:提防隐式转换带来的麻烦
- 改善C++ 程序的150个建议学习之建议20:使用memcpy()系列函数时要足够小心
- 改善C++ 程序的150个建议学习之建议7:时刻提防内存溢出
- 改善C++ 程序的150个建议学习之建议2:避免那些由运算符引发的混乱
- 改善C++ 程序的150个建议学习之建议22:灵活地使用不同风格的注释
- 改善C++ 程序的150个建议学习之建议8:拒绝晦涩难懂的函数指针
- 改善C++ 程序的150个建议学习之建议18:正确区分void与void*
- 改善C++ 程序的150个建议学习之建议7:时刻提防内存溢出
- 改善C++ 程序的150个建议学习之建议 9:防止重复包含头文件
- 改善C++ 程序的150个建议学习之建议10:优化结构体中元素的布局
- 改善C++ 程序的150个建议学习之建议23:尽量使用C++标准的iostream
- 改善C++ 程序的150个建议学习之建议19:明白在C++中如何使用C
- 改善C++ 程序的150个建议学习之建议32:借助工具监测内存泄漏问题