编写new和delete时的规则

既然是规则那就重点研究一下是什么规则:

例程:

 1 void* operator new(std::size_t size) throw(std::bad_alloc){
 2     using namespace std;
 3     if (size == 0)
 4         size = 1;
 5     while (true){
 6         尝试分配 size bytes;
 7         if (分配成功)
 8             return (一个指针  指向分配得来的内存);
 9 
10         //分配失败;找出目前的new-handling 
11         new_handler globalHandler = set_new_handler(0);
12         set_new_handler(globalHandler);
13 
14         if (globalHandler) (*globalHandler)();
15         else throw std::bad_alloc();
16     }
17 }

以上代码透露出一个标准的operator new应该做的几件事儿:

  1. 如果分配的字节大小是0,将其改为1.
  2. 里面包含一个死循环 出循环的条件是:
    1. 分配内存成功。
    2. 分配失败异常终止,抛出 bad_alloc的异常,这里之前侯捷老师的课里面有介绍,后面再看一遍来补充。
  3. set_new_handler 函数是在这里被运行的,关于运行set_new_handler 的意义之前说过,作为一个异常处理函数 主要有以下几件事儿需要做:
    1. 让更多的内存可用
    2. 安装另一个new_handler函数。
    3. 承认失败直接结束程序。

这里在运行异常处理函数的时候用了 (*globalHandler) (),用函数指针调用了该函数,在c++中,函数和函数指针之间的转换是可以的,可以不用*直接调用。

例程:

 1 void ff(){
 2     std::cout << "runing me" << std::endl;
 3 }
 4 
 5 typedef void(*func_ptr)();
 6 int _tmain(int argc, _TCHAR* argv[]){
 7     
 8     func_ptr f_ptr = ff;
 9     func_ptr f_ptr1 = &ff;
10     f_ptr();
11     (*f_ptr)();
12     f_ptr1();
13     (*f_ptr1)();
14     return 0;  
15 }

运行结果:

 

 

议题二:重载new在继承体系中体现出的异常行为

1 class Base{
2 public:
3     static void* operator new(std::size_t size) throw(std::bad_alloc);
4     ...
5 };
6 class Derived : public Base{ ... };
7 Derived* p = new Derived;

以上代码第7句执行的new操作将运行 base::operator new 这个函数可能引起内存分配异常,原因如下:

这个函数里面可能针对base做了一些特定的操作,但是这些特定的操作并不适合 Derived。

改进方式如下:

1 void* Base::operator new(std::size_t size) throw(std::bad_alloc){
2     if (size != sizeof(Base))
3         return ::new_handler(size);
4     ...
5 }

通过判断大小来确定待分配对象的具体大小再分别处理。这里成立的理由是 表达式 new将待分配对象的大小传给operator new 相当于operator new实际上是由表达式new在调用。

但是上面的方法对new[ ] 是行不通的,理由如下:

  1. 根据size参数无法判断分配数据类型,因为size大小由分配个数和类型共同决定。
  2. 传递给operator new的size_t参数,其值可能比可能比待分配的类型数组的大小要大,因为动态分配的arry operator需要额外的空间来存放元素个数等额外信息

议题三:重载operaor delete的规矩

规矩很简单:保证删除 空指针 NULL的安全性。

1 void operator delete(void* rawMemory) throw(){
2     if (rawMemory == 0) return;
3     下面归还其它情况
4 }

简单的判断,区别对待。

关于这个内容有一个有意思的话题:当在继承体系中,而基类没有定义自己的虚析构函数是可能引发的异常:

这时,调用delete表达式传给 operator new的size可能是不对的,之前学过 delete 的两个步骤 先析构再 再operator delete 这里的大小是由delete来传递的。

 

posted @ 2020-06-09 15:13  熊鑫xxx1x  阅读(176)  评论(0)    收藏  举报