zoukankan      html  css  js  c++  java
  • 动态对象创建(二)重载new和delete

    动态对象创建(二)重载newdelete

    前言

    上文我简单介绍了一下动态对象创建的方法,这一篇文章的内容主要是对重载newdelete做一些讲解,也希望能够得到博友们的指点,在这里谢过大家。

    通常我们为了一些目的而使用newdelete的内存分配系统,但是在特殊情况下,它并不能够满足需要。最常见的改变分配系统的原因是出于效率考虑:也许要创建和销毁一个特定的类的非常多的对象以至于这个运算变成了速度的瓶颈。C++允许重载newdelete来实现我们自己的存储分配方案,所以可以用它来处理问题。

    另一个问题就是堆碎片:分配不同大小的内存可能会在堆上产生很多碎片,以至于很快用完内存。虽然内存可能还有,但是由于都是碎片,也就找不到足够大的内存块满足需要。通过为特定类创建自己的内存分配器,可以确保这种情况不会发生。

    当我们在重载operator new()operator delete()时,我们只是改变了原有的内存分配方法。编译器将用重载的new代替默认版本去分配内存,然后为那个内存调用构造函数。所以,虽然当编译器看到new时,编译器分配内存并调用构造函数,但是当重载new时,可以改变的只是内存分配部分。

    接下来,我分为三个部分详细讲解重载newdelete重载全局newdelete对于一个类重载newdelete以及为数组重载newdelete

    重载全局newdelete

    当我们重载全局的newdelete的时候,可想而知,就会使默认版本完全不能被访问--甚至在这个重新定义里也不能调用它们。

    重载的new必须有一个size_t参数。这个参数由编译器产生并传递给我们,它是要分配内存的对象的长度。必须返回一个指向等于这个长度(或者大于这个长度)的对象的指针。如果没有找到存储单元(在这种情况下,构造函数不被调用),则返回一个0。然后如果找不到存储单元,不能仅仅返回0,我们还应该调用new-handler或产生一个异常信息之类的事,告诉这里出现了问题。

    我们首先需要明白的一点就是operator new()的返回值是一个void*,而不是指向任何特定类型的指针。所做的是分配内存,而不是完成一个对象建立--直到构造函数调用了才完成对象的创建,它是编译器确保做的动作,不在我们的控制范围之内了,所以我们就没有必要考虑。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<cstdlib>
     5 #include<cmath>
     6 #include<algorithm>
     7 #define inf 0x7fffffff
     8 using namespace std;
     9 
    10 void* operator new(size_t sz)
    11 {
    12     printf("operator new: %d Bytes
    ",sz);
    13     void* m = malloc(sz);
    14     if (!m) puts("out of memory");
    15     return m;
    16 }
    17 
    18 void operator delete(void* m)
    19 {
    20     puts("operator delete");
    21     free(m);
    22 }
    23 
    24 class S
    25 {
    26 public:
    27     S() {puts("S::S()"); }
    28     ~S() {puts("S::~S()"); }
    29 private:
    30     int an[1000];
    31 };
    32 
    33 int main()
    34 {
    35     puts("creating & destroying an int");
    36     int* q = new int(23);
    37     delete q;
    38     puts("creating & destroying an int[]");
    39     int* p = new int[10]();
    40     delete []p;
    41     puts("creating & destroying an s");
    42     S* s = new S;
    43     delete s;
    44     puts("creating & destroying S[3]");
    45     S* sa = new S[3];
    46     delete []sa;
    47     return 0;
    48 }

    对于一个类重载newdelete

    为一个类重载newdelete的时候,尽管不必显式的使用static,但是实际上仍是在创建static成员函数。它的语法也和重载任何其它运算符一样。当编译器看到使用new创建自己定义的类的对象时,它选择成员版本的operator new()而不是全局版本的new()。但是全局版本的newdelete仍为所有其他类型对象使用(除非它们也有自己的newdelete)。这个和全局变量、局部变量的意思是一样的,应该很好懂吧。

     1 #include<iostream>
     2 #include<cstddef>
     3 #include<fstream>
     4 #include<new>
     5 using namespace std;
     6 ofstream out("Framis.out");
     7 
     8 class Framis
     9 {
    10 public:
    11     enum{psize = 100 };
    12     Framis() {out<< "Framis()" <<endl; }
    13     ~Framis() {out<< "~Framis() ... " <<endl; }
    14     void* operator new(size_t) throw (bad_alloc);
    15     void operator delete(void*);
    16 private:
    17     enum{sz = 10 };
    18     char c[sz];
    19     static unsigned char pool[];
    20     static bool alloc_map[];
    21 };
    22 unsigned char Framis::pool[psize*sizeof(Framis)];
    23 bool Framis::alloc_map[psize]={false};
    24 
    25 void* Framis::operator new(size_t sz) throw(bad_alloc)
    26 {
    27     for (int i=0; i<psize; ++i) {
    28         if (!alloc_map[i]) {
    29             out<< "using block " << i << " ... ";
    30             alloc_map[i]=true;
    31             return pool+(i*sizeof(Framis));
    32         }
    33     }
    34     out<< "out of memory" <<endl;
    35     throw bad_alloc();
    36 }
    37 
    38 void Framis::operator delete(void* m)
    39 {
    40     if (!m) return;
    41     unsigned long block = (unsigned long)m-(unsigned long)pool;
    42     block /= sizeof(Framis);
    43     out<< "freeing block " << block <<endl;
    44     alloc_map[block]=false;
    45 }
    46 
    47 int main()
    48 {
    49     Framis* f[Framis::psize];
    50     try {
    51         for (int i=0; i<Framis::psize; i++) {
    52             f[i]=new Framis;
    53         }
    54         new Framis;
    55     } catch(bad_alloc) {
    56         cerr<< "Out of memory!" <<endl;
    57     }
    58     delete f[10];
    59     f[10]=0;
    60     Framis* X=new Framis;
    61     delete X;
    62     for (int j=0; j<Framis::psize; j++) {
    63         delete f[j];
    64     }
    65     return 0;
    66 }

    为数组重载newdelete

    上一段文字中我们讲到如果为一个类重载operator new()operator delete(),那么无论何时创建这个类的一个对象都将调用这些运算符。但是如果要创建这个类的一个对象数组的时候,全局operator new()就会被立即调用,用来为这个数组分配足够的内存。对此,我们可以通过为这个类重载运算符的数组版本,即operator new[]operator delete[],来控制对象数组的内存分配。

     1 #include<iostream>
     2 #include<fstream>
     3 #include<new>
     4 using namespace std;
     5 ofstream trace("ArrayOperatorNew.out");
     6 
     7 class Widget
     8 {
     9 public:
    10     Widget() {trace<< "*" <<endl; }
    11     ~Widget() {trace<< "~" <<endl; }
    12     void* operator new(size_t sz) {
    13         trace<< "Widget::new: " << sz << " byte" <<endl;
    14         return ::new char[sz];
    15     }
    16     void operator delete(void* p) {
    17         trace<< "Widget::delete" <<endl;
    18         ::delete []p;
    19     }
    20     void* operator new[](size_t sz) {
    21         trace<< "Widget::new[]: " << sz << " bytes" <<endl;
    22         return ::new char[sz];
    23     }
    24     void operator delete[](void* p) {
    25         trace<< "Widget::delete[]" <<endl;
    26         ::delete []p;
    27     }
    28 private:
    29     enum{sz=10 };
    30     int an[sz];
    31 };
    32 
    33 int main()
    34 {
    35     trace<< "new Widget" <<endl;
    36     Widget* w=new Widget;
    37     trace<<endl<< "delete Widget" <<endl;
    38     delete w;
    39     trace<<endl<< "new Widget[25]" <<endl;
    40     Widget* wa=new Widget[25];
    41     trace<<endl<< "delete []Widget" <<endl;
    42     delete []wa;
    43     return 0;
    44 }

    总结

    1:对于C++中的动态对象创建又有了新的认识,学习了重载new和delete

    2:C++空类的大小是1

    3:最让我激动的就是:C++程序把运行结果写入新创建的文档里面,这个和ACM里常用的文件读写还是不一样滴。

    好吧,继续努力!!!

  • 相关阅读:
    .net core 学习小结之 配置介绍(config)以及热更新
    .net core 学习小结之环境配置篇
    powershell下载网站图片
    Powershell 脚本输出前十条消耗内存的进程到excel
    Linux 自学shell
    使用bat脚本进行开机启动批处理
    Git 创建分支并合并主分支
    Git 的使用及其一些基本用法
    点击按钮复制文本到剪切板
    关于一些基本排序的实现
  • 原文地址:https://www.cnblogs.com/BaiYiShaoNian/p/4681367.html
Copyright © 2011-2022 走看看