Re: [問題] sizeof大小變動的class

看板C_and_CPP作者 (閉上眼的魚)時間13年前 (2012/07/04 09:24), 編輯推噓1(107)
留言8則, 3人參與, 最新討論串2/2 (看更多)
這問題其實蠻有興趣的,早期私下有想過,我猜有其他較好的處理方式, 由於動作較低階,實作較複雜,過程大多只能以虛擬碼討論。 前半段為回應,後半段是小弟在較低階的想法,供討論、參考。 ----------------------------------                 回  應 ※ 引述《oneill (安福虎之助)》之銘言: : class data{ : public: : int score; : char *name; : }; sizeof(class) 其實和 sizeof(struct) 一樣,只是裡面的 function 都沒算 size, 以此例而言, sizeof(data) = sizeof(int) + sizeof(char*) + padding 就像下面這段一樣 struct{ int score; char * name; }; : 然後用sizeof求大小 : sizeof(d1); : sizeof(d2); 如 james~ 大所言,你要做的事情大概是要重寫一份 Constructor 及 operator = (assigned operator) , 看任何一本 C++ 之書籍都會說到, 有用到 new / malloc 之類的東西就一定要寫兩部份 (1) Constructor, Destructor (2) assigned operator (operator = ) 另即使你要做 memcpy 時,真的做到的只有這部份 data a, b; memcpy(&a, &b, sizeof(data); a.score = b.score; a.name = b.name; 只有複製 pointer ,如果你要做的是 n 筆資料,共享一份記憶體,這可以, 但相對的在釋放的時候只能放一次,不然釋放 n 次肯定出包。 這種沒辦法進一步連 name allocate 之 memory 深入複製 統稱 shallow copy,C/C++ 做的都只是 shallow copy。要達到你想要的只能這麼做 data& operator=(const data& d) const{ /* 先判斷 strlen(name) 夠不夠放 strlen(d.name), 做記憶體管理 */ strcpy(name, d.name); score = d.score;   return *this;  } 這種連 heap 上之記憶體都深入複製的稱 deep copy,C\C++ 沒辦法做到這點, 所以只能自己動手刻。 當你自己實際動手刻完後會發現,你還是回歸到用固定大小, 或 C++ class string 好了,因在記憶體掌控效能方面, 基本上寫不怎贏 string 。 其他的可再找一下 deep copy / shallow copy 差異, < C++ 不熟的話複習一下 operator overload > ------------------------------------ 以下二個小技巧,是基於一信念 : allocate / release 次數愈少愈好。                 小 技 巧 1   我不確定實際上資料庫 txt field 通常是多長, 但可以確定的是,即使 txt field 不長,每個欄位給 char txt[256], 到了 10 萬筆資料 (10^5,這個數字在資料庫裡應還算是中小型資料庫) 時, 吃了約 25 MB, 這時候要塞到 stack 也很危險,就算真要固定欄位也該放在 heap 上。 依「課本」上 ( 其實我只是聽說的,不知道哪個課本) 的技巧, (1) capacity > length 時, realloc, capacity*=2. (2) capacity < 0.25*length 時, realloc, capacity/=2. 個人多加了一個技巧 : rate 因 capacity*=2 最後會愈來愈大,難保不會 allocate fail. 所以我是這麼做的 double multiple = 2.0; double rate = 0.95; size_t cap = 128 * multiple; char *new_str; do{ new_str = allocate(str, cap); cap*=rate; }while(rate > 1.0 && new_str==NULL) if(new_str!=NULL) str = new_str; 甚至覺得 2.0 增長實在太快,所以策略換成 (1) capacity > length 時, realloc, capacity*=1.25. (2) capacity < 0.64*length 時, realloc, capacity/=1.25. < 0.64 = 1 / 1.25 / 1.25 > 但實際上有拆過 vector, string 都知道,在開 O2 後,他們根本都不這麼做, 不然就不會有 vector resize(0) 後,記憶體一直佔住的情況。 < 他們的策略是什麼我倒蠻好奇的,非到緊急關頭誓不放人 嗎? >  小 技 巧 2  雖說是「小技巧」,其實要完整實作很費時 ( 特別是以 C++ 完成及針對多行緒時 ) 但我猜有被使用,甚至比我所提的、實作出來的更加成熟, 為方便討論,假設每個欄位長度限制是固定的 256 。 一開始 heap 上切一塊 pool 出來, pool = malloc(1U<<28) , 256MB, 一個 pool 有 256 MB, 所以它容許放 1M (1U<<20) 的資料量。 甚至這個 pool 用 linklist 較佳,因不夠的時候還可以再 allocate 另一塊, 而另一個 pool allocate 不出來的時候,再將 pool 改小一點 , (回到技巧一的動態縮減技巧)。 再來會有一個 bool used[1M] (可用 1bit 紀錄改善),紀錄該 field 是否有被使用過, 正在使用的話就放進去 used[i] 換成 true, 要刪掉時只要將 used[i] 改成 false, used[i]==true 成立,代表 pool[256*i] ~ pool[256*(i+1)] 正被使用, 要取得也很簡單 char * get_str(size_t i) { return pool + i*256; /* return pool + (i<<8); */ } 但關鍵就變成了,怎麼知道那個 string 用的是 used[???] 這裡就可擅用 C++ class 特性了,原本的 class data{ int score ; char * name; }; 就換成了 class data{ int score; size_t used_idx; }; 如此只需在 constructor , destructor 時做 used 更新。 但問題又來了,如果將 txt field limit 拿掉的話呢? 這時需要其他的 array (linklist) 儲存資訊了, 如紀錄目前此 field 是從 pool[start : pos]、 pool 裡最大的連續空間是多少等等之類的,做起來反而更麻煩, 做不好的話讓 os 自己管理還比較好 。 ----------------------------------- 以上一點意見,供參考。有誤請不吝更正。 -- 「自從我學了 C# , 人都變聰明 , 考試都考一百分」 「自從我學了 VB , 皮膚都變好 , 人也變漂亮了 」 「自從我學了 Java , 明顯變壯 , 個子也變高了 」 「自從我學了 C++ , 內分泌失調 , 頭都禿了... 」 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 180.177.76.161 ※ 編輯: EdisonX 來自: 180.177.76.161 (07/04 17:34)

07/04 20:25, , 1F
vector.resize(0) 應該要改成 vector().swap(vector)
07/04 20:25, 1F

07/04 20:26, , 2F
才能釋放資源, resize(0) 本來就不能釋放記憶體空間
07/04 20:26, 2F

07/04 20:30, , 3F
我個人覺得原po想對非POD的資料做memcpy這個動作本身就不對
07/04 20:30, 3F

07/04 21:38, , 4F
這篇文章很多假設「頂多」POD對(至於POD對不對還沒細看)
07/04 21:38, 4F

07/04 21:39, , 5F
同意 damody 最後一行,POD 本來就不能這樣惡搞...
07/04 21:39, 5F

07/04 21:42, , 6F
原Po: 除了 padding 以外還有很多東西,那個等號是錯的。
07/04 21:42, 6F

07/04 21:43, , 7F
應該說那個等號所傳達的概念在 C++ 中很多時候都是錯的
07/04 21:43, 7F

07/04 22:01, , 8F
謝謝指正。
07/04 22:01, 8F
文章代碼(AID): #1Fz0lWla (C_and_CPP)
討論串 (同標題文章)
文章代碼(AID): #1Fz0lWla (C_and_CPP)