動態創建對象

分類: C++2011-04-28 14:55 680人閱讀 評論(0) 收藏 舉報

=============================================================

標題:動態創建對象

日期:2011.4.27

文獻:Thinking in C++

姓名:朱銘雷

=============================================================

———————————————————————————————————————

內存分配

———————————————————————————————————————

動態創建一個類的實例對象(於堆中)——採用c庫函數

步驟

1 調用malloc申請內存,特別要注意所要申請內存的大小。

2 檢驗返回指針,確保內存成功分配。

3 初始化對象。

可以看出,採用c庫函數動態創建類的對象很不方便,易出錯。

———————————————————————————————————————

operator new (new操作符)

CBookInfo *pBookInfo = new CBookInfo;

new操作符會首先調用malloc分配內存,並且會檢查內存分配是否成功。之後將所申請的內存地址,傳遞給CBookInfo類的構造函數,以初始化這塊內存。總之,它簡化了“動態申請內存”,也同時減少了出錯機率。

———————————————————————————————————————

operator delete (delete操作符)

與new配對使用。

delete pBookInfo;

pBookInfo = NULL; // #define NULL    0

delete操作符首先調用CBookInfo類析構函數,然後調用free釋放內存。將pBookInfo置爲NULL(0),是爲了防止刪除pBookInfo指針兩次,對同一個對象刪除delete兩次可能會出現問題,而“delete 0;”是安全的,因爲它什麼都不做。

———————————————————————————————————————

delete void*

void *pBookInfo = new CBookInfo;

delete pBookInfo;

pBookInfo = NULL;

這裏的delete操作只會釋放CBookInfo對象內存,但不會調用CBookInfo析構函數,因爲編譯器不知道該去調用哪個析構函數。

———————————————————————————————————————

動態創建對象時,小心使用void指針

在使用之前,應將其轉換爲適當(實際)類型,否則可能會丟失類型信息。在刪除之前也要將其轉換爲適當(實際)類型,否則可能會產生內存泄露。

void *p = new string("abc");

cout << *(string*)p << endl;

delete (string*)p;

p = NULL;

———————————————————————————————————————

operator delete用於數組

CBookInfo *pBookInfo = new CBookInfo[10];

delete pBookInfo;

pBookInfo = NULL;

編譯器不知道pBookInfo實際上存儲的是指向一個動態創建的數組的起始地址,所以最終只對數組中的第一個CBookInfo對象調用了析構函數。正確的書寫方式:

delete [] pBookInfo;

方括號的作用是使編譯器取回動態創建數組時存放在某處的對象數量,以便爲數組中所有的對象調用析構函數。

———————————————————————————————————————

new-handler

當new操作符無法找到足夠大的內存時,將調用new-handle函數,該函數默認產生一個異常。程序員可以調用標準庫函數set_new_handler來設置自己的new-handler。

#include <new>

void out_of_memory()

{

  cerr << "memory exhausted" << endl;

  exit(1);

}

set_new_handler(out_of_memory);

———————————————————————————————————————

重載new和delete

上面已經知道,new操作符將首先使用operator new()分配內存,然後調用對象的構造函數。構造函數的調用由編譯器完成,程序員可以改變的只是如何分配內存。重載delete的情況也是類似。

———————————————————————————————————————

重載全局的new和delete

重載全局的new和delete要慎重,將導致默認版本完全不能被訪問,出了問題就是很嚴重的問題。

operator new()和operator delete()的normal signature:

void* operator new(size_t size);

void operator delete(void *p);

示例:

void* operator new(size_t size)

{

  void* m = malloc(sz);

  if(!m) puts("out of memory");

  return m;

}

void operator delete(void* m)

{

  free(m);

}

———————————————————————————————————————

爲了一個特定的類重載new和delete

在一個類中重載new和delete,即使不顯示使用static關鍵字,它們也仍然是一個靜態的成員函數。如果一個類A重載了new,則使用new A創建對象時將使用類A的重載new而不是全局new。delete的情況也一樣。如果某個類重載了new和delete,通常也會重載new[]和delete[],否則“new A[10];”將會調用全局new。

———————————————————————————————————————

一個完整的類重載new,new[],delete,delete[]示例

#include <new>

#include <fstream>

using namespace std;

ofstream trace("ArrayOperatorNew.out");

class Widget

{

       static const int sz = 2;

       int i[sz];

public:

       Widget() { trace << "Widget()/n"; }

       ~Widget() { trace << "~Widget()/n"; }

       void* operator new(size_t sz)

       {

              trace << "Widget::new: " << sz << " bytes/n" << endl;

              return ::new char[sz];

       }

       void* operator new[](size_t sz)

       {

              trace << "Widget::new[]: " << sz << " bytes/n" << endl;

              return ::new char[sz];

       }

       void operator delete(void* p)

       {

              trace << "Widget::delete/n" << endl;

              ::delete []p;

       }

       void operator delete[](void* p)

       {

              trace << "Widget::delete[]/n" << endl;

              ::delete []p;

       }

};

 

int main()

{

       Widget* w = new Widget;

       delete w;

       Widget* wa = new Widget[2];

       delete []wa;

}

看下ArrayOperatorNew.out文件的輸出內容:

Widget::new: 8 bytes

Widget()

~Widget()

Widget::delete

Widget::new[]: 20 bytes

Widget()

Widget()

~Widget()

~Widget()

Widget::delete[]

從輸出信息可以看出:

1 new(new[])操作會首先調用Widget::new(Widget::new[])分配內存,然後調用Widget()構造函數初始化內存。

2 delete(delete[])操作會首先調用~Widget()析構函數,然後調用Widget::delete(Widget::delete

[])釋放內存。

3 “new Widget[2]”實際申請了20個字節,額外多申請了4個字節,這4個字節用來保存數組信息,尤其是數組中存放的對象數量,以備delete []使用。

———————————————————————————————————————

重載多參數的new和delete

如:

void* operator new(size_t s, char* pszFileName, int iLineNum);

void operator delete(void* p, char* pszFileName, int iLineNum);

第一個參數和返回值類型不要改動,比如new的第一個參數,由編譯器計算需要分配多少內存,傳遞給new。

———————————————————————————————————————

在MFC中重載全局new和delete

這個會有所不同,可能會遇到一定的麻煩。

———————————————————————————————————————

發佈了22 篇原創文章 · 獲贊 2 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章