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 <ext\pool_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 <ext\pool_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 << "\ntest_per_class_allocator_2().......... \n";
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\n\n", b[0], b[1], b[2], b[3]);
120     cout << p[1] << ' ' << p[1]->getType() << ' ' << p[1]->getMiles()<< "\t\t" <<(void*)256 << endl;
121     cout << p[5] << ' ' << p[5]->getType() << ' ' << p[5]->getMiles()<< "\t\t" <<(void*)1024 << endl;
122     cout << p[9] << ' ' << p[9]->getType() << ' ' << p[9]->getMiles()<< '\t' <<(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()<<"\tNext =";
131         cout.width(8);                  // 設置域寬為8
132         cout<<p[i]->getNext();
133 
134         b = (unsigned char*)p[i];
135         printf("\tp[%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\n", 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 << "\nglobal new test_per_class_allocator_2().......... \n";
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()<<"\t Next =\t"<<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 <ext\pool_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++


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM