• 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++

  • 相关阅读:
    api示例
    Windows+Ubuntu文件互传
    UI
    事件
    插件开发入门
    文摘
    刘海屏适配
    APT
    热修复
    进程保活
  • 原文地址:https://www.cnblogs.com/flysong/p/8167371.html
Copyright © 2020-2023  润新知