【1】malloc與free 和 new與delete
(1)malloc與free是C語言的標准庫函數。new與delete是C++的運算符。它們都可以申請與釋放動態內存。
(2)對於非內部數據類型的對象而言,用malloc與free無法滿足動態對象的要求(對象在創建的同時要自動執行構造函數,對象在消亡之前要自動執行析構函數)。
(3)由於malloc/free是庫函數而不是運算符,不在編譯器控制權限之內,不能夠把執行構造函數和析構函數的任務強加於malloc/free。
因此,C++語言需要可以完成動態內存分配與初始化工作的運算符new,以及一個能完成清理與釋放內存工作的運算符delete。
(4)都是在堆(heap)上進行動態的內存操作。用malloc函數需要指定內存分配的字節數並且不能初始化對象。new會自動調用對象的構造函數。
delete會調用對象的destructor,而free不會調用對象的destructor。
【2】描述內存分配方式以及它們的區別?
(1)從靜態存儲區域分配。靜態內存區域在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都一直存在。例如:全局變量,static 變量的存儲區域。
(2)在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集。
(3)從堆上分配(動態內存分配)。程序在運行的時候用malloc 或 new 申請任意多少的內存,程序員自己負責在何時用free 或 delete 釋放內存。
動態內存的生存期由程序員決定,使用非常靈活,但問題也最多。
【3】malloc 與 free
(1)malloc函數分配的空間一定要用free函數釋放掉。
(2)free(p) 僅僅指釋放了malloc分配的空間,但是p指針仍然不為空,所以,在free函數釋放后一般要置空!防止野指針!
(3)非空指針只可以釋放一次。
(4)一般兩者搭配使用。
(5)malloc 與 free示例代碼如下:
1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 5 void main() 6 { 7 int *p = NULL ; // 指針定義最好初始化為空(程序員基本素養) 8 9 // 空指針釋放多次沒有任何意義 10 free(p); // 一次!編譯通過!運行通過! 11 free(p); // 二次!編譯通過!運行通過! 12 13 p = (int *)malloc(sizeof(int) * 5); 14 if (NULL == p) 15 { 16 cout << "malloc failed!" << endl; 17 exit(1); 18 } 19 else 20 { 21 p[0] = 0; // 注意賦值形式 22 p[4] = 4; 23 // p[5] = 100; // 編譯可以通過,但是運行錯誤error::因為p[5]越界 24 25 cout << "p[4]: " << p[4] << endl; // 4 26 27 cout << "p[1]: " << p[1] << endl; // 隨機數!! 28 } 29 30 free(p); // malloc申請空間使用free釋放(固定搭配) 31 32 if (NULL == p) 33 { 34 cout << "free(p) 后 p == NULL" << endl; 35 } 36 else 37 { 38 cout << "free(p) 后 p != NULL" << endl; // 野指針!!! 39 // p[2] = 100; // 編譯可以通過,運行時崩潰!!因為野指針所致。 40 } 41 42 // free(p); // 編譯可以通過,運行時崩潰!因為已經釋放了一次,再次釋放導致錯誤。 43 44 p = NULL; // 徹底預防了它的隱患 45 46 //........ 47 system("pause"); 48 } 49 50 // run out: 51 /* 52 p[4]: 4 53 p[1]: -842150451 54 free(p) 后 p != NULL 55 請按任意鍵繼續. . . 56 */
【4】new 與 delete
(1)new的三種形態
到目前為止,C++相關資料書籍談及的new至少代表以下三種含義:
<1> new operator : new 運算符 (當然,書面稱法。個人覺得還是按照習慣稱作關鍵字new,以下此種形態均稱關鍵字 new)
<2> operator new : 操作符 new(當然,書面稱法。個人覺得稱為new函數,以下此種形態均稱new函數)
<3> placement new: 安置 new(C++primer上的稱法)
(2)關鍵字new
平常我們使用最多的就是關鍵字new。它由語言內建,不能重載,不能改變其行為。
關鍵字new在堆上動態創建一個對象時,它實際上做了三件事:
1:申請獲得一塊動態內存空間
2:調用對象的構造函數初始化對象內容
3:返回目的指針,即構建對象所申請的動態內存空間的指針
當然,如果我們創建的是內置類型的變量,那么第二步會被省略。
示例代碼如下:
1 #include <iostream> 2 #include <cassert> 3 using namespace std; 4 5 class A 6 { 7 int i; 8 9 public: 10 A (int _i = 2) : i(_i * _i) 11 { 12 cout << "constructor " << this << endl; 13 } 14 void Print() 15 { 16 cout << i << endl; 17 } 18 ~A() 19 { 20 cout << "destructor " << this << endl; 21 } 22 }; 23 24 void main() 25 { 26 /* 27 * 內置類型示例代碼 28 */ 29 int *p1 = NULL; 30 p1 = new int(10); 31 assert(p1 != NULL); 32 cout << *p1 << endl; // 10 33 delete p1; 34 p1 = NULL; 35 36 37 int *p2 = NULL; 38 p2 = new int[5]; // 申請5份int類型大小的空間 39 assert(p2 != NULL); 40 delete []p2; // 釋放數組變量 41 p2 = NULL; 42 43 /* 44 * 自定義類型示代碼 45 */ 46 A *p3 = NULL; 47 p3 = new A; // 調用默認復合構造函數 48 assert(p3 != NULL); 49 p3->Print(); // 4 50 cout << "delete obj" << endl; 51 delete p3; 52 p3 = NULL; 53 54 A *p4 = NULL; 55 p4 = new A[5]; // 調用默認復合構造函數 注意數組 56 assert(p4 != NULL); 57 p4[0].Print(); // 4 58 cout << "delete obj" << endl; 59 delete []p4; 60 p4 = NULL; 61 62 A *p5 = NULL; 63 p5 = new A(10); // 調用復合默認構造函數 注意區別 64 assert(p5 != NULL); 65 p5[0].Print(); // 100 66 cout << "delete obj" << endl; 67 delete p5; 68 p5 = NULL; 69 70 system("pause"); 71 } 72 73 // run out: 74 /* 75 10 76 constructor 00514890 77 4 78 delete obj 79 destructor 00514890 80 constructor 00514924 81 constructor 00514928 82 constructor 0051492C 83 constructor 00514930 84 constructor 00514934 85 4 86 delete obj 87 destructor 00514934 88 destructor 00514930 89 destructor 0051492C 90 destructor 00514928 91 destructor 00514924 92 constructor 00514890 93 100 94 delete obj 95 destructor 00514890 96 請按任意鍵繼續. . . 97 */
(3)函數new
關鍵字new第一步分配內存實際上是通過調用new函數來完成的,而這里的new就是像加減乘除一樣的操作符,因此是可以重載的。
new函數默認情況下首先調用分配內存的代碼,嘗試得到一段堆上的空間,如果成功就返回;如果失敗,則轉而去調用一個new_hander,然后繼續重復前面過程。
如果我們對這個過程不滿意,就可以重載operator new,來設置我們希望的行為。
示例代碼如下:
1 #include <iostream> 2 #include <cassert> 3 using namespace std; 4 5 class A 6 { 7 int i; 8 9 public: 10 A (int _i = 2) : i(_i * _i) 11 { 12 cout << "constructor " << this << endl; 13 } 14 void Print() 15 { 16 cout << i << endl; 17 } 18 ~A() 19 { 20 cout << "destructor " << this << endl; 21 } 22 }; 23 24 /* 25 * 重載全局new/delete函數 26 */ 27 void * operator new(size_t size) 28 { 29 cout << " overload operator new " << endl; 30 void *p = malloc(size); 31 return (p); 32 } 33 34 void operator delete(void *p) 35 { 36 cout << " overload operator delete " << endl; 37 free(p); 38 } 39 40 void main() 41 { 42 /* 43 * 內置類型示例代碼 44 */ 45 int *p1 = NULL; 46 p1 = (int *)::operator new(sizeof(int)); 47 new(p1) int(10); // 第一種賦值方式 48 assert(p1 != NULL); 49 cout << *p1 << endl; // 10 50 ::operator delete(p1); 51 p1 = NULL; 52 53 54 int *ptr = NULL; 55 ptr = (int *)::operator new(sizeof(int)); 56 *ptr = 100; // 第二種賦值方式 57 assert(ptr != NULL); 58 cout << *ptr << endl; // 100 59 delete ptr; 60 ptr = NULL; 61 62 int *p2 = NULL; 63 p2 = (int *)::operator new(sizeof(int) * 5); // 申請5份int類型大小的空間 64 assert(p2 != NULL); 65 for (int i = 0; i < 5; ++i) 66 { 67 p2[i] = i + 10; // 數組變量的賦值 68 } 69 for (int i = 0; i < 5; ++i) 70 { 71 cout << p2[i] << endl; // 10 11 12 13 14 72 } 73 ::operator delete[] (p2); // 釋放數組變量 74 p2 = NULL; 75 76 /* 77 * 自定義類型示例代碼 78 */ 79 A *p3 = NULL; 80 p3 = (A *)::operator new(sizeof(A)); 81 assert(p3 != NULL); 82 new(p3) A(10); 83 p3->Print(); // 100 84 cout << "delete obj" << endl; 85 p3->~A(); // 先調用對象析構函數 86 ::operator delete(p3); // 再釋放申請內存 87 p3 = NULL; 88 89 // 注意差別 90 A *p4 = NULL; 91 p4 = (A *)::operator new(sizeof(A)); 92 assert(p4 != NULL); 93 new(p4) A(10); 94 p4->Print(); // 100 95 cout << "delete obj" << endl; 96 ::operator delete(p4); // 直接釋放申請內存 97 p4 = NULL; 98 99 system("pause"); 100 } 101 102 // run out: 103 /* 104 overload operator new 105 10 106 overload operator delete 107 overload operator new 108 100 109 overload operator delete 110 overload operator new 111 10 112 11 113 12 114 13 115 14 116 overload operator new 117 constructor 00434890 118 100 119 delete obj 120 destructor 00434890 121 overload operator delete 122 overload operator new 123 constructor 00434890 124 100 125 delete obj 126 overload operator delete 127 請按任意鍵繼續. . . 128 */
以上這段代碼建議最好調試逐步看看,詳細分析一下運行結果,然后認真總結一下。
下面比較new關鍵字與new函數的區別:
<1> new關鍵字
int *ptr = new int(100);
1:分配內存; 2:賦初始值; 3:類型自動匹配; 4:大小自動。
<2> new函數
int *ptr = (int *)::operator new(sizeof(int) * 5);
1:分配內存; 2:無初始化; 3:類型轉換; 4:大小手動。
(4)安置new
關鍵字new可以說為了應用的方便性考慮,其本質也是調用new函數進行內存配置的,然后再根據申請類型創建並初始化對象具體內容。
那么,假如試想一下這個情境:我們現在已申請到了一塊內存,但臨時突然想在這塊內存空間上構建另一個對象呢?好,安置new當仁不讓。
安置new是用來實現定位構造的,因此可以實現關鍵字new三步操作中的第二步:
也就是,在取得了一塊可以容納指定類型對象(變量)的內存后,在這塊內存上構造一個對象(變量)。
示例代碼如下:
關於對象的構建,上面new函數的示例代碼中已經很具體。在此,特別示例定位new也可以構造棧上的內存。
1 #include <iostream> 2 #include <cassert> 3 //#include<new.h> //有些資料書提醒必須加這個頭文件,VS2010下可以省略。 4 using namespace std; 5 6 class A 7 { 8 int i; 9 10 public: 11 A (int _i = 2) : i(_i * _i) 12 { 13 cout << "constructor " << this << endl; 14 } 15 void Print() 16 { 17 cout << i << endl; 18 } 19 ~A() 20 { 21 cout << "destructor " << this << endl; 22 } 23 24 }; 25 26 27 void main() 28 { 29 char s[sizeof(A)]; 30 A* p = (A*)s; 31 new(p) A(3); // 定位new的用法 32 p->Print(); 33 p->~A(); // 不過必須要顯式調用析構函數 34 system("pause"); 35 } 36 37 // run out: 38 /* 39 constructor 001DFCA8 40 9 41 destructor 001DFCA8 42 請按任意鍵繼續. . . 43 */
這里“new(p) A(3)”這種“奇怪的”寫法即是安置new的用法,它實現了在指定內存空間用指定類型的構造函數來構造一個對象的功能,后面A(3)就是對構造函數的顯式調用。
通過上面的例子,我們可以看到這塊指定的地址既可以是棧內存,又可以是堆內存,安置new對此不加區分。
但是,除非特別必要,不要直接使用安置new ,這畢竟不是用來構造對象的正式寫法,只不過是new函數的一個步驟而已。
使用關鍵字new地編譯器會自動生成對安置new的調用的代碼,因此也會相應的生成使用delete時調用析構函數的代碼。
如果是像上面那樣在棧上使用了安置new,則必須手工調用析構函數,這也是顯式調用析構函數的唯一情況: p->~A();
當我們覺得默認的關鍵字new對內存的管理不能滿足我們的需要,而希望自己手工的管理內存時,安置new就有用了。
STL中的allocator就使用了這種方式,借助安置new來實現更靈活有效的內存管理。
【5】new的基本使用指南
(1)如果想在堆上建立一個對象,應該用關鍵字new 。它既分配內存又為對象調用構造函數。
(2)如果僅僅想分配內存,就應該調用 new 函數;它不會調用構造函數。
如果想定制在堆對象被建立時的內存分配過程,你應該寫自己的new 函數,然后使用new關鍵字, new 關鍵字會調用定制的 operator new .
(3)如果想在一塊已經獲得指針的內存里建立一個對象,應該用 安置new 。安置new 主要適用於:
<1> 在對時間要求非常高的應用程序中,因為這些程序分配的時間是確定的;
<2> 長時間運行而不被打斷的程序;
<3> 以及執行一個垃圾收集器(garbage collector)。
Good Good Study, Day Day Up.
順序 選擇 循環 堅持 總結