zoukankan      html  css  js  c++  java
  • c++ 内存管理

      c++中给对象分配内存常见有三种方法:

    • 使用c++ 库函数 std::allocator  (c++ library);
    • 使用new,new[] 表达式,::operator new() 操作符,(c++ primitives);
    • c 函数 malloc/free (CRT);

    测试代码如下:

     1 #include<iostream>
     2 #include <stdlib.h>
     3 #include<complex>
     4 #include <memory>    //内含 std::allocator
     5 #include <extpool_allocator.h> // __pool_alloc
     6 
     7 using namespace std;
     8 
     9 void test()
    10 {
    11     void* p1 = malloc(512); //512bytes
    12     free(p1);
    13 
    14     complex<int>* p2 = new complex<int>; //one object
    15     delete p2;
    16 
    17     void* p3 = ::operator new(512);// 512bytes
    18     ::operator delete(p3);
    19 
    20 #ifdef __GNUC__
    21     //以下函数都是non-static,一定要通过object调用,分配7个int的内存
    22     void* p4 = allocator<int>().allocate(7);//allocator<int>()创建临时对象
    23     allocator<int>().deallocate((int*)p4,7);
    24 
    25     //void* p5 = alloc::allocate(512);  //2.9
    26     //alloc::deallocate(p5,512);
    27 
    28     void* p6 = __gnu_cxx::__pool_alloc<int>().allocate(9); // 对应上面的 gnc2.9;
    29     __gnu_cxx::__pool_alloc<int>().deallocate((int*)p6,9);
    30 #endif // __GNUC__
    31 
    32 #ifdef _MSC_VER_
    33     int* p6 = allocator<int>().allocate(3, (int*)0);
    34     allocator<int>().deallocate(p6,3);
    35 #endif // _MSC_VER_
    36     return;
    37 }
    View Code

    一、new

    使用new表达式,编译器将其转化为先调用 operator new 运算符,然后调用构造函数。 new过程是先分配内存,然后调用构造函数;delete时,先调用析构函数,然后释放内存。

    image image
    只有编译器可以直接调用构造函数,示例代码如下:
    class A
    {
    public:
        int id;
    
        A() : id(0)
        {
            cout << "default ctor.this=" << this << " id=" << id << endl;
        }
        A(int i):id(i)
        {
            cout<< "ctor. this = " << this <<" id=" <<id <<endl;
        }
        ~A()
        {
            cout<<"dtor.this = "<<this <<endl;
        }
    };
    void testCt()
    {
        string* pstr = new string;
        cout << "str = " << *pstr <<endl;
    
        // pstr->string::string("123"); //'class std::basic_string<char>' has no member named 'string'|
        pstr->~string();
        cout<<"str = " <<endl;
    
        A* pA = new A(1);
        cout<< "pA->id = "<< pA->id<<endl;
    
        // pA->A::A(3);//error: cannot call constructor 'A::A' directly|
    
        // A::A(5); //error: cannot call constructor 'A::A' directly [-fpermissive]|
    
        cout<< "pA->id = "<<pA->id<<endl;
        delete pA;
        A* pA2;
        new(pA2)A(5);
        cout<< "pA2->id = "<<pA2->id<<endl;
        delete pA2;
    }
    View Code

    image

    二、Array new, Replacement new 

    image 
      定义数组A* p = new A[3];时,会申请分配内存,并调用三次默认构造函数;
      当使用 delete[] p;时,会调用三次析构函数,并释放内存,而使用delete p,则数组所占内存仍然会释放掉,但只会调用一次析构函数,而如果析构函数内有释放内存的操作,则使用delete p,造成内存泄漏。
    测试代码如下:
     1 void testArrNew()
     2 {
     3     A* buf = new A[3]; //默认构造函数调用3次 调用顺序 0-1-2
     4     //A必须有默认构造函数 new A[3]调用的是默认构造函数
     5 
     6     A* tmp = buf;//记录A数组的起点位置
     7 
     8     cout << "buf=" << buf << " tmp=" << tmp << endl;
     9 
    10     for(int i = 0; i < 3; i++)
    11     {
    12         new(tmp++)A(i); //placement new;在分配好的内存上,赋值
    13     }
    14 
    15     cout << "buf=" << buf << " tmp=" << tmp << endl;
    16 
    17     //delete[] buf;
    18     delete buf;
    19 }
    View Code

    执行结果如下:

    image image

    三、 placement new

    先看一下placement new

    char* buf = new char[sizeof(A) * 3];//申请内存
    
    A* pc = new(buf)A();//在申请好的buf的内存,在buf上赋值

    代码中 new(buf)A(); 就是placement new.

    编译器会将上述代码转化为

    A * pc;
    try {
        void* men = operator new(sizeof(A), buf); //申请内存
        pc = static_cast<A*>(mem);//转换
        pc->A::A();//构造函数
    }
    catch (std::bad_alloc){
      
    }

    四、重载 operator new, operator new[], operator delete, operator delete[]

    接管全局new,delete 函数,重载使用自己的操作符。

    测试代码如下:

      新建class Foo类

     1 class Foo
     2 {
     3 private:
     4     int _id;
     5     long _data;
     6     string _str;
     7 
     8 public:
     9     Foo():_id(0)
    10     {
    11         cout << "default ctor.this=" << this << " id=" << _id << endl;
    12     }
    13     Foo(int a):_id(a)
    14     {
    15         cout << "ctor.this=" << this << " id=" << _id << endl;
    16     }
    17 
    18     virtual
    19     ~Foo()
    20     {
    21         cout << "dtor.this=" << this << " id=" << _id << endl;
    22     }
    23 
    24     //申请内存的函数必须是静态的 调用这个函数时一般都是正在创建这个对象
    25     //所以当调用时,这个对象还不存在,需要声明成静态
    26     static void* operator new(size_t size);
    27     static void  operator delete(void* pdead, size_t size);
    28     static void* operator new[](size_t size);
    29     static void  operator delete[](void* pdead, size_t size);
    30 };
    31 
    32 void* Foo::operator new(size_t size)
    33 {
    34     Foo* p = (Foo*)malloc(size);
    35     cout <<"operator new().size="<< size << "       return=" << p <<endl;
    36     return p; //p 为内存起始点
    37 }
    38 
    39 void Foo::operator delete(void* pdead, size_t size)//pdead 删除点,和上面的p为同一个位置,size 为将要删除的内存大小
    40 {
    41     cout <<"operator delete.pdead=" << pdead << "       size=" << size <<endl;
    42     cout << endl;
    43     free(pdead);
    44 }
    45 
    46 void* Foo::operator new[](size_t size)
    47 {
    48     Foo* p = (Foo*)malloc(size);
    49     cout <<"operator new[].size="<< size <<"     return=" << p << endl;
    50     return p;
    51 }
    52 
    53 void Foo::operator delete[](void* pdead, size_t size)
    54 {
    55     cout<< "operator delete[].pdead=" << pdead << "     size="<< size <<endl;
    56     cout << endl;
    57     free(pdead);
    58 }
    class Foo

      测试函数

    1 void testoperatornew()
    2 {
    3     cout << "sizeof(Foo)="<<sizeof(Foo) << endl;
    4     Foo* p = new Foo(7);
    5     delete p;
    6 
    7     Foo* pArray = new Foo[5];
    8     delete [] pArray;
    9 }
    View Code

      执行结果

    image

    重载new(),delete()

        可以重载class member operator new(), 其中第一个参数必须是size_t,其余参数以new所指定的placement arguments 为初值,出现于new() 括号内的就是所谓的placems arguments.

         也可以重载class member operator delete(),但他们不会被delete调用,只有当new所调用的ctor抛出异常,才会调用重载版的operator delete(),主要用来释放未完全创建成功的object所占有的内存。

    示例代码

     1 class Foo2
     2 {
     3 private:
     4     int _id;
     5 
     6 public:
     7     Foo2()
     8     {
     9         cout << " Foo2()::Foo2() "  << endl;
    10     }
    11     Foo2(int a)
    12     {
    13         cout << "Foo2()::Foo2(int) "<< endl;
    14         throw Bad();
    15     }
    16 
    17 
    18     void* operator new(size_t size)
    19     {
    20         cout << "operator new(size_t size),  size="<< size <<endl;
    21         return malloc(size);
    22     }
    23 
    24     // 标准库提供的placeent new()的重载形式
    25     void* operator new(size_t size, void* star)
    26     {
    27         cout << "operator new(size_t size),  size="<< size<< " star = " << star <<endl;
    28         return malloc(size);
    29     }
    30     // 模拟标准库的形式,只传回pointer
    31     void* operator new(size_t size, long extra)
    32     {
    33         cout << "operator new(size_t size, long extra),  size="<< size <<" extra = " << extra<<endl;
    34         return malloc(size+extra);
    35     }
    36 
    37     void* operator new(size_t size, long extra,char init)
    38     {
    39         cout << "operator new(size_t size, long extra,char init),  size="<< size <<" extra = " << extra
    40              <<" init = " << init<<endl;
    41         return malloc(size+extra);
    42     }
    43     /* 又一个 ,但故意写错第一参数类型
    44       void* operator new(long extra, char init) //error: 'operator new' takes type 'size_t' ('unsigned int')
    45     {
    46         return malloc(extra);                   //as first parameter [-fpermissive]|
    47     } */
    48 
    49     void operator delete(void*,long)
    50     {
    51         cout<<" operator delete(void*,size_t) " <<endl;
    52     }
    53     void operator delete(void*,long,char)
    54     {
    55         cout<<" operator delete(void*,long,char) " <<endl;
    56     }
    57 };
    58 
    59 void testFoo2()
    60 {
    61     Foo2 start;
    62     Foo2* p1= new Foo2;
    63     Foo2* p2= new(&start) Foo2;
    64     Foo2* p3= new(100) Foo2;
    65     Foo2* p4= new(100,'A') Foo2;
    66     Foo2* p5= new(100) Foo2(1);
    67     Foo2* p6= new(100,'A') Foo2(1);
    68     Foo2* p7= new(&start) Foo2(1);
    69     Foo2* p8= new Foo2(1);
    70 }
    View Code

    运行结果

    image五、内存池

    内存池的优点

    1.减少malloc的使用,提高运行效率

    2.减少内存碎片,减少cookie

    构造简单的内存池,示例代码如下

      1 #include<iostream>
      2 #include <stdlib.h>
      3 #include<complex>
      4 #include <memory>    //内含 std::allocator
      5 #include <extpool_allocator.h> // __pool_alloc
      6 
      7 using namespace std;
      8 
      9 class Screen
     10 {
     11 public:
     12     Screen(int x): i(x)  { }
     13     int get() {    return i; }
     14 /************************重载 ××××*************/
     15     void* operator new(size_t);
     16     void operator delete(void*, size_t);
     17 /* **********************重载 ××××************ ***/
     18 private:
     19     Screen* next;//4bit
     20     static Screen* freeStore;
     21     static const int screenChunk;//想要创建多少组
     22 
     23 private:
     24     int i; //4bit
     25 };
     26 
     27 Screen* Screen::freeStore = 0;
     28 const int Screen::screenChunk = 24;
     29 
     30 /************************重载×××××××××××××××××××××××××*************/
     31 
     32 void* Screen::operator new(size_t size)
     33 {
     34     Screen* p;
     35     if(!freeStore)
     36     {
     37         //linked list是空的,所以申请一大块内存
     38         size_t chunk = screenChunk * size; //192 Screen的内存大小为8共24组  24 * 8 = 192
     39         freeStore = p =
     40                 reinterpret_cast<Screen*>(new char[chunk]);
     41         cout << "startPisotion: " << p << endl;
     42 
     43         //将一大块内存分割成片段,当做linked list串接起来
     44         for(; p != &freeStore[screenChunk-1]; ++p)
     45         {
     46             p->next = p+1;
     47         }
     48         p->next = 0;
     49     }
     50     p = freeStore;
     51     freeStore = freeStore->next;
     52 
     53     return p;
     54 }
     55 
     56 void Screen::operator delete(void* p, size_t)
     57 {
     58     //将delete object插回 free list前端
     59     (static_cast<Screen*>(p)) -> next = freeStore;
     60     freeStore = static_cast<Screen*>(p);
     61 }
     62 /************************重载×××××××××××××××××××××××××**************/
     63 void test_per()
     64 {
     65     cout << "sizeof(int)"<< sizeof(int*) << endl;
     66     cout << "sizeof(Screen*)"<< sizeof(Screen*) << endl;
     67     cout << "sizeof(Screen)"<< sizeof(Screen) << endl;
     68 
     69     size_t const N = 10;
     70 
     71     Screen* p[N];
     72 
     73     cout << "overload operator new" << endl;
     74     for(int i=0; i<N; i++)
     75     {
     76         p[i] = new Screen(i);
     77     }
     78 
     79     for(int i = 0; i<10; i++)
     80     {
     81         cout << p[i] << endl;//输出每个Screen的内存起点
     82     }
     83 
     84     for(int i=0; i<N; i++)
     85     {
     86         delete p[i];
     87     }
     88 
     89     cout << "glob operator new" << endl;
     90 
     91     Screen* q[N];
     92 
     93     for(int i=0; i<N; i++)
     94     {
     95         q[i] = ::new Screen(i);
     96     }
     97 
     98     for(int i = 0; i<10; i++)
     99     {
    100         cout << q[i] << endl;
    101     }
    102 
    103     for(int i=0; i<N; i++)
    104     {
    105         ::delete q[i];
    106     }
    107 }
    108 
    109 int main()
    110 {
    111     test_per();
    112     return 0;
    113 }
    View Code

    测试结果

    image image

      左边是重载了member operator new/delete 的结果,右边是没有使用重载而是使用global operator new/delete 的结果。class Screen 大小为8字节,使用重载的函数数组内相邻元素地址相隔8个字节,减少了使用malloc时的cookie;而不使用重载函数,相隔48个字节。

      上述例子中,类中多出了一个指针Screen* next;//4bit ,增加了内存开销,可以采用union减少内存开销,代码如下: 

      1 //ref. Effective C++ 2e, item10
      2 //per-class allocator
      3 
      4 class Airplane     // customized memory management
      5 {
      6 private:
      7     struct AirplaneRep
      8     {
      9         unsigned long miles;
     10         char type;
     11     };
     12 private:
     13     union
     14     {
     15         AirplaneRep rep;  //此针对 used object
     16         Airplane* next;   //此针对 free list
     17     };
     18 public:
     19     unsigned long getMiles()
     20     {
     21         return rep.miles;
     22     }
     23     char getType()
     24     {
     25         return rep.type;
     26     }
     27     void set(unsigned long m, char t)
     28     {
     29         rep.miles = m;
     30         rep.type = t;
     31     }
     32     void* getNext()
     33     {
     34         return next;
     35     }
     36 public:
     37     static void* operator new(size_t size);
     38     static void  operator delete(void* deadObject, size_t size);
     39 private:
     40     static const int BLOCK_SIZE;
     41     static Airplane* headOfFreeList;
     42 };
     43 
     44 Airplane* Airplane::headOfFreeList;
     45 const int Airplane::BLOCK_SIZE = 128;
     46 
     47 void* Airplane::operator new(size_t size)
     48 {
     49     //如果大小错误,转交给 ::operator new()
     50     if (size != sizeof(Airplane))
     51         return ::operator new(size);
     52 
     53     Airplane* p = headOfFreeList;
     54 
     55     //如果 p 有效,就把list头部移往下一个元素
     56     if (p)
     57         headOfFreeList = p->next;
     58     else
     59     {
     60         //free list 已空。配置一块够大内存,
     61         //令足够容纳 BLOCK_SIZE 个 Airplanes
     62         Airplane* newBlock = static_cast<Airplane*>
     63                              (::operator new(BLOCK_SIZE * sizeof(Airplane)));
     64         //组成一个新的 free list:将小区块串在一起,但跳过
     65         //#0 元素,因为要将它传回给呼叫者。
     66         for (int i = 1; i < BLOCK_SIZE-1; ++i)
     67             newBlock[i].next = &newBlock[i+1];
     68         newBlock[BLOCK_SIZE-1].next = 0; //以null结束
     69 
     70         // 将 p 设至头部,将 headOfFreeList 设至
     71         // 下一个可被运用的小区块。
     72         p = newBlock;
     73         headOfFreeList = &newBlock[1];
     74     }
     75     return p;
     76 }
     77 
     78 // operator delete 接获一块内存。
     79 // 如果它的大小正确,就把它加到 free list 的前端
     80 void Airplane::operator delete(void* deadObject,
     81                                size_t size)
     82 {
     83     if (deadObject == 0) return;
     84     if (size != sizeof(Airplane))
     85     {
     86         ::operator delete(deadObject);
     87         return;
     88     }
     89 
     90     Airplane *carcass =
     91         static_cast<Airplane*>(deadObject);
     92 
     93     carcass->next = headOfFreeList;
     94     headOfFreeList = carcass;
     95 }
     96 
     97 //-------------
     98 void test_per_class_allocator_2()
     99 {
    100     cout << "
    test_per_class_allocator_2().......... 
    ";
    101 
    102     cout << sizeof(Airplane) << endl;    //8
    103 
    104     size_t const N = 20;
    105     Airplane* p[N];
    106 
    107     for (int i=0; i< N; ++i)
    108         p[i] = new Airplane;
    109 
    110 
    111     //随机测试 object 正常否
    112     p[1]->set(256,'A');
    113     p[5]->set(1024,'B');
    114     p[9]->set(256000,'C');
    115 
    116     unsigned char* b = (unsigned char*)p[1];
    117     printf("二进制p[1] low: %02X%02X%02X%02X", b[0], b[1], b[2], b[3]);
    118     b=b+4;
    119     printf(" high: %02X%02X%02X%02X
    
    ", b[0], b[1], b[2], b[3]);
    120     cout << p[1] << ' ' << p[1]->getType() << ' ' << p[1]->getMiles()<< "		" <<(void*)256 << endl;
    121     cout << p[5] << ' ' << p[5]->getType() << ' ' << p[5]->getMiles()<< "		" <<(void*)1024 << endl;
    122     cout << p[9] << ' ' << p[9]->getType() << ' ' << p[9]->getMiles()<< '	' <<(void*)256000 << endl<<endl;
    123 
    124     cout<<"--地址---type---miles--------Next-------------二进制----------"<<endl;
    125     //输出前 10 个 pointers, 用以比较其间隔
    126     for (int i=0; i< 10; ++i)
    127     {
    128         cout << p[i]<< "  " << p[i]->getType() << "  " ;
    129         cout.width(8);                  // 设置域宽为8
    130         cout<< p[i]->getMiles()<<"	Next =";
    131         cout.width(8);                  // 设置域宽为8
    132         cout<<p[i]->getNext();
    133 
    134         b = (unsigned char*)p[i];
    135         printf("	p[%d] L:%02X%02X%02X%02X",i, b[3], b[2], b[1], b[0]);
    136         b=b+4;
    137         printf(" H:%02X%02X%02X%02X
    ", b[3], b[2], b[1], b[0]);
    138     }
    139 
    140 
    141     for (int i=0; i< N; ++i)
    142         delete p[i];
    143 
    144     cout << "
    global new test_per_class_allocator_2().......... 
    ";
    145     cout <<endl;
    146 
    147     for (int i=0; i< N; ++i)
    148         p[i] = ::new Airplane;
    149 
    150     //随机测试 object 正常否
    151     p[1]->set(256,'A');
    152     p[5]->set(1024,'B');
    153     p[9]->set(256000,'C');
    154     cout << p[1] << ' ' << p[1]->getType() << ' ' << p[1]->getMiles()<< ' ' <<(void*)256 << endl;
    155     cout << p[5] << ' ' << p[5]->getType() << ' ' << p[5]->getMiles()<< ' ' <<(void*)1024 << endl;
    156     cout << p[9] << ' ' << p[9]->getType() << ' ' << p[9]->getMiles()<< ' ' <<(void*)256000 << endl;
    157     cout <<endl;
    158     //输出前 10 个 pointers, 用以比较其间隔
    159     for (int i=0; i< 10; ++i)
    160     {
    161         cout << p[i]<< ' ' << p[i]->getType() << ' ' ;
    162         cout.width(8);                  // 设置域宽为8
    163         cout<< p[i]->getMiles()<<"	 Next =	"<<p[i]->getNext()<< endl;
    164     }
    165     for (int i=0; i< N; ++i)
    166         ::delete p[i];
    167 }
    View Code

      测试结果如下

    test_per_class_allocator_2

     

    六、static allocate

    上节分配内存的方法,每个类中都要重载new,delete; 因此将该部分提取出来,封装为一个类allocate; 将应用类的实现与内存分配细节分离开来

    测试代码

      1 #include<iostream>
      2 #include <stdlib.h>
      3 #include<complex>
      4 //#include <memory>    //内含 std::allocator
      5 //#include <extpool_allocator.h> // __pool_alloc
      6 
      7 using namespace  std ;
      8 
      9 class myAllocator
     10 {
     11 private:
     12     struct obj
     13     {
     14         struct obj* next;
     15     };
     16 
     17 public:
     18     void* allocate(size_t);
     19     void  deallocate(void*, size_t);
     20 private:
     21     obj* freeStore = nullptr;
     22     const int CHUNK = 5; // 便于观察,设为5
     23 };
     24 
     25 void myAllocator::deallocate(void* p, size_t size)
     26 {
     27     cout << "myAllocator::deallocate" << "size: " << size <<endl;
     28     ((obj*)p)->next = freeStore;
     29     freeStore = (obj*)p;
     30 }
     31 
     32 void* myAllocator::allocate(size_t size)
     33 {
     34    // cout << "myAllocator::allocate" << "size: " << size <<endl;
     35     obj* p;
     36     if(!freeStore)
     37     {
     38         size_t chunk = CHUNK * size;
     39         freeStore = p = (obj*)malloc(chunk);
     40 
     41         for(int i=0; i<(CHUNK-1); ++i)
     42         {
     43             p->next = (obj*)((char*)p + size);
     44             p = p->next;
     45         }
     46 
     47         p->next = nullptr;
     48     }
     49     p= freeStore;
     50     freeStore = freeStore -> next;
     51 
     52     return p;
     53 }
     54 
     55 class Foo
     56 {
     57 public:
     58     long L;
     59     string str;
     60     static myAllocator myAlloc;
     61 public:
     62     Foo(long l): L(l)
     63     {
     64         //todo
     65     }
     66 
     67     static void* operator new(size_t size)
     68     {
     69         return myAlloc.allocate(size);
     70     }
     71 
     72     static void operator delete(void* pdead, size_t size)
     73     {
     74         return myAlloc.deallocate(pdead, size);
     75     }
     76 };
     77 myAllocator Foo::myAlloc;
     78 
     79 class Goo
     80 {
     81 public:
     82     complex<double> L;
     83     string str;
     84     static myAllocator myAlloc;
     85 public:
     86     Goo(const complex<double>& l): L(l)
     87     {
     88         //todo
     89     }
     90 
     91     static void* operator new(size_t size)
     92     {
     93         return myAlloc.allocate(size);
     94     }
     95 
     96     static void operator delete(void* pdead, size_t size)
     97     {
     98         return myAlloc.deallocate(pdead, size);
     99     }
    100 };
    101 myAllocator Goo::myAlloc;
    102 
    103 void test3()
    104 {
    105     size_t const N = 20;
    106     Foo* p[N];
    107 
    108     cout << "overload operator new" << endl;
    109     cout <<"sizeof(Foo) = "<< sizeof(Foo) << endl;
    110 
    111     for(int i=0; i<N; i++)
    112     {
    113         p[i] = new Foo(i);
    114         cout<<p[i] << ' '<<p[i]->L<<endl;
    115     }
    116 
    117     for(int i = 0; i<N; i++)
    118     {
    119         cout << p[i] << endl;
    120     }
    121 
    122     for(int i=0; i<N; i++)
    123     {
    124         delete p[i];
    125     }
    126 
    127     cout << "glob operator new" << endl;
    128 
    129     Foo* q[N];
    130 
    131     for(int i=0; i<N; i++)
    132     {
    133         q[i] = ::new Foo(i);
    134     }
    135 
    136     for(int i = 0; i<N; i++)
    137     {
    138         cout << q[i] << endl;
    139     }
    140 
    141     for(int i=0; i<N; i++)
    142     {
    143         ::delete q[i];
    144     }
    145 
    146     Goo* pG[N];
    147 
    148     cout << "overload operator new" << endl;
    149     cout <<"sizeof(Goo) = "<< sizeof(Goo) << endl;
    150 
    151     for(int i=0; i<N; i++)
    152     {
    153         pG[i] = new Goo(complex<double>(i,i));
    154         cout<<pG[i] << ' '<<pG[i]->L<<endl;
    155     }
    156 
    157     for(int i=0; i<N; i++)
    158     {
    159         delete pG[i];
    160     }
    161 }
    162 
    163 int main()
    164 {
    165     test3();
    166     return 0;
    167 }
    View Code

    执行结果

    image image

    myAllocator 中const int CHUNK = 5; (便于观察,设为5),块内每5个元素地址相差一个元素大小,块与块之间地址相差较大。

    优化升级

    上述示例代码中,使用myAllocator的格式是固定的,因此可以将其提取出来定义为宏。

    代码如下:

     1 // DECLARE_POOL_ALLOC() --used in class definition
     2 #define DECLARE_POOL_ALLOC()
     3 public:
     4     void* operator new(size_t size){ myAlloc.allocate(size); }
     5     void operator delete(void* p){ myAlloc.deallocate(p, 0); } 
     6 protected:
     7     static myAllocator myAlloc;
     8 
     9 // IMPLEMENT_POOL_ALLOC() --used in class implemention file
    10 #define IMPLEMENT_POOL_ALLOC(class_name)
    11 myAllocator class_name::myAlloc;
    12 
    13 class Foo
    14 {
    15     DECLARE_POOL_ALLOC()
    16 public:
    17     long L;
    18     string str;
    19 public:
    20     Foo(long l): L(l)
    21     {
    22         //todo
    23     }
    24 };
    25 IMPLEMENT_POOL_ALLOC(Foo)
    26 
    27 class Goo
    28 {
    29     DECLARE_POOL_ALLOC()
    30 public:
    31     complex<double> L;
    32     string str;
    33 public:
    34     Goo(const complex<double>& l): L(l)
    35     {
    36         //todo
    37     }
    38 };
    39 IMPLEMENT_POOL_ALLOC(Goo)
    View Code

    七、何时重载operator new或operator delete

       用来检测运用上的错误。如果delete new的内存失败,会导致内存泄漏。如果在new所得内存多次delete会导致不确定行为。使用编译器提供的operator new和operator delete不能检测上述行为。如果operator new持有一个链表,其存储动态分配所得内存,operator delete则将内存从链表删除,这样就能检测上述错误用法。如果编程错误,可能在分配内存的之前区域或之后区域写入数据;这时可以自己定义operator new分配超额内存,在多出部分写上特定byte patterns(即签名,signature),operator delete检测签名是否更改,是否发生了overrun 或 underrun。

      为了强化效能。编译器所带的operator new和operator delete主要用于一般目的,它处理的内存请求有时很大,有时很小,它必须处理大数量短命对象的持续分配和归还。它们必须考虑碎片问题。定制版的operator new和operator delete通常在性能上胜过缺省版本,它们运行得比较快,需要的内存比较少。

      为了收集使用上的统计数据。在重载之前,首先要了解软件如何使用动态内存。分配区块如何分布?寿命如何?它们是FIFO先进先出还是LIFO后进先出,或随机分配和归还?软件在不同执行阶段有不同的分配归还形态吗?任何时刻使用的最大动态分配量是多少?自己定义的operator new和operator delete可以轻松收集到这些信息。

      为了增加分配和归还的速度。使用定制的针对特定类型对象的分配器,可以提高效率。例如,Boost提供的Pool程序库便是。如果在单线程程序中,你的编译器所带的内存管理具备线程安全,你可以写个不具备线程安全的分配器而大幅度改善速度。

      为了降低缺省内存管理器带来的空间额外开销。泛用型分配器往往(虽然并非总是)不只比定制型慢,还使用更多空间,因为它们常常在每一个分配区块上招引某些额外开销。针对小型对象开放的分配器,例如Boost库的Pool,本质上消除了这样的额外开销。

      为了弥补缺省分配器的非最佳对齐(suboptimal alignment)。X86体系结构上的double访问最快–如果它们是8-byte对齐。但是编译器自带的operator new并不保证分配double是8-byte对齐。

      为了将相关对象成簇集中。如果特定的某个数据结构往往被一起使用,我们希望在处理这些数据时将“内存页错误”(page faults)的频率降至最低,那么为此数据结构创建另一个heap就有意义,这样就可以将它们成簇集中到尽可能少的内存也上。

      为了获得非传统的行为。有时候我们需要做operator new和delete没做的事。例如,在归还内存时将其数据覆盖为0,以此增加应用程序的数据安全。

      

    上述所有代码执行环境为GNU4.9.2

    内容参考自:侯捷c++内存管理课程, Effective c++

  • 相关阅读:
    二分图最大匹配的K&#246;nig定理及其证明
    HDOJ 2389 Rain on your Parade
    HDOJ 1083 Courses
    HDOJ 2063 过山车
    POJ 1469 COURSES
    UESTC 1817 Complete Building the Houses
    POJ 3464 ACM Computer Factory
    POJ 1459 Power Network
    HDOJ 1532 Drainage Ditches
    HDU 1017 A Mathematical Curiosity
  • 原文地址:https://www.cnblogs.com/flysong/p/8167371.html
Copyright © 2011-2022 走看看