作者tomap41017 (絕夢)
看板EE_DSnP
標題Re: [問題] delete和delete[]
時間Wed Oct 13 01:06:12 2010
剛剛用open office編輯結果它當了,害我要重打,要養成存檔的好習慣。
老師下課說在版上去年這時候應該有文章,不過我找了好久都找不到阿,
後來翻閱一下手邊的工具書,覺的應該是POD造成的問題(Plain Old Data)。
寫個小程式測試一下:
#include <iostream>
#include <vector>
using namespace std;
class POD {
int i;
};
class NonPOD {
double i;
~NonPOD() {
}
};
template<class T>
void test() {
typedef T* Pointer;
Pointer p = new T[10];
void *v = reinterpret_cast<void*> (p);
size_t* size = reinterpret_cast<size_t*> (v - sizeof(size_t));
cout << *size << endl;
}
int main() {
test<POD> ();
test<int> ();
test<std::vector<int> > ();
test<NonPOD> ();
}
此程式在我的電腦編譯(g++4.4.3)執行結果為:
49
49
10
10
什麼是POD呢?簡單說就是class沒有定義constructor/copy constructor/assignment
operator/destructor這四種,完全靠compiler generated版本(這個版本也稱作
trivial – constructor/....),那是POD跟這個array刪除有什麼關聯阿?
這可以從這次作業HW1.2.2這題看起,在Vector中,我們要初始化_data這個pointer,
我們會寫_data = new unsigned[size]; 如果你沒有用一個迴圈去初始化每個值,當直接
讀取
時,有時後會得到垃圾值。若我們想要讓他們都能夠初始化成unsigned的預設值呢?
可以寫:
_data = new unsigned[size]();
我們再看到Matrix,在constructor內要初始化_data,不過這時候問題來了:
_data = new Vector[numRows]; 會呼叫Vector的哪一個constructor阿?
如果你有用_data[1].size()去讀出它的值,會發現是0,也就是
Vector::Vector(unsigned = 0);被呼叫了。
我們可以看到,在初始化一個陣列時我們只能(implicitly or explicitly)呼叫其
default
constructor,我們從int的例子可以發現,不加上()時,constructor不會被呼叫,
而不會把每個元素的初始值設為零;而在Vector的例子,編譯器有看到default
constructor,因此判定它不是POD,implicitly呼叫每個元素的default ctor。
(當你把在Vector::Vector的預設參數拿掉時,編譯器就會唉啦:你沒給我一個
default constructor我怎麼建構阿?而且你已經寫了constructor了,
我更不可能幫你寫一個阿!!)
這就是編譯器對待不同類型的type會有不同的處理方式!!
如果你的class沒有寫任何constructor,編譯器就會幫你寫一份,但那份
裡面什麼都不做。
(對copy ctor跟assignment operator而言則是 memner wise assignment,
所以你呼叫Vector a(b);//利用另一個Vector b來建構a
是會通過編譯的,只不過在a,b被摧毀時delete 2 _data指的記憶體。
)
好,重點來了,對於destructor而言,編譯器幫你寫得那份,有要作什麼用嗎?
答案是:沒有,而且對於內建型別(int double)而言也是一樣,
所以在上面的測試程式中,我們看到POD, int得到的是相似結果,
而std::vector<int>與NonPOD得到的是10的結果,也就是陣列大小。
編譯器知道陣列大小要作什麼用阿?答案就是在摧毀陣列時明白呼叫其destructor囉!
for(int I = 0; I < size; ++i){
_data[I].~Vector();
}
而沒有定義destructor的POD,只要把記憶體刪除,這個物件也就掰了~~
所以int[]與POD[]都不需要存陣列大小。
※ 引述《puerpuella (柏亨)》之銘言:
我在寫作業的時候對delete產生了一點疑惑...
我本來以為delete是刪除一個object用的,而delete[]是刪除多個用的
但是我在main()中宣告
double* d = new double[1];
之後不管我寫delete d; 或delete []d; 執行都會成功
而且就算我是宣告double* d = new double[0]; 時也可以
可是我寫了一個class叫做C,在main()中
C* c = new C[1];
delete c;
這樣執行就不過了,一定要用delete[]才OK
是因為宣告時如果有用[ ],delete的時候就要加嗎?
可是在double的時候好像不用加[]也行?
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 140.112.245.114
1F:推 ric2k1:new [] 的變數用 delete 只會 delete 其中的一個,其他的 10/11 22:20
2F:推 ric2k1:會變成 memory leak. 照理來說不會馬上 crash,如果會crash 10/11 22:20
3F:→ ric2k1:應該是其他原因 10/11 22:21
4F:推 ric2k1:簡單的說: 用 new [] 就要用 delete [], 反之用 new 則要用 10/11 22:22
5F:→ ric2k1:delete. 10/11 22:22
6F:→ ric2k1:明天上課會再說明一下。 10/11 22:23
7F:→ puerpuella:謝謝教授!!我後來又試了一下如果我C裡面沒寫destructor 10/11 22:34
8F:→ puerpuella:就不會有事,可是一加上~C()就掛了.. 10/11 22:34
9F:推 ric2k1:這個跟去年的 HW1.2 P3 有關... 我明天來一起講解一下! 10/11 23:04
10F:推 johnjohnlin:要作 deep copy 吧?我之前也這樣 10/12 15:50
簡單講應該也是deep copy啦XDDDDDDDDd
附註一下:
Exceptional C++ (我是看國際中文版,英文版應該是3e?)
Item 36:有寫到
一般的compiler在destructor通常的實做方式為,
會在dtor函式碼尾端有一個不可見旗標,
如果此變數是一個auto物件(非new出來的)則此旗標為false
若是一個dynamic物件,則此旗標為true
這個旗標代表的是『當這物件被摧毀時,我該刪除它嗎?』
如果為true,則在尾端呼叫正確(就是該class)的operator delete()
(忘了說,class defined operator delete隱喻為static)
若class B有virtual dtor,且也有(static) B::operator delete(void*);
class D為其derived class,但只定義(static) D::operator delete(void*);
則
D* ptr = new D;
delete ptr;//喚起D::operator delete(void*)
但
B* ptr2 = new D;
delete ptr2;//因為有virtual dtor,喚起~D(),
~D()內則一樣喚起D::operator delete(void*);
所以compiler其實有幫我們作一些事情的(在trivial內)。
※ 編輯: tomap41017 來自: 140.112.244.171 (10/13 01:17)
11F:推 ric2k1:推一個!! 抱歉,我說的文章是 #2451 10/13 01:11
12F:→ tomap41017:突然覺的我寫得落落長 囧 10/13 01:19
13F:推 ric2k1:不會啊! 講得很清楚!! 感謝 10/13 01:41
14F:推 aitjcize:大推! 10/14 00:49