歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux資訊 >> 更多Linux

模擬虛構造函數的內存分配優化

  //轉貼自我的朋友雲風的一篇文章, //裡面有些DarkSpy自己寫的注釋,希望能給不太懂這篇文章意思的朋友一些提示。 構造函數不能是虛的, 這讓人很郁悶. 在 Thinking in C++ 第2版的最後作者給出了一種模擬虛構造 函數的方法, 基本是這樣的. 代碼:-------------------------------------------------------------------------------- // 給出一個抽象類 shape, 裡面有要提供的接口 class shape { public: shape(); virtual ~shape(); virtual void draw(); //.... }; // 別的類用這個派生 class circle : public shape{ public: circle(); ~circle(); void draw(); //... }; class rectangle : public shape { public: rectangle(); ~rectangle(); void draw(); //... }; // 再給一個 shapewrap 封裝一下 class shapewarp { protected: shape *object; public: shapewrap(const string &type) { if (type=="circle") object=new circle; else if (type=="rectangle") object=new rectangle; else { // ... } } ~shapewrap() { delete object; } void draw() { object->draw(); } }; -------------------------------------------------------------------------------- 我昨天在做腳本的參數分析的時候, 想給出一個類似 vb 或者 Java 裡那樣的 var 類型, 能夠裝下所有不同種類的變量. 基本上的要求更上面的例子很像. 但是出於效率的角度, 考慮到 wrap 類僅僅只有 4 字節, 放了一個對象指針. 無論在何地構造出 wrap 對象, 都會有一個動態的 new 操作 做內存分配, 如果參數表用 stl 的容器裝起來, 這些 new 操作 做的內存分配也無法用到 stl 容器的比較高效的內存管理策略. 這讓人心裡很不舒服, 所以就著手優化這一部分的代碼. 開始的核心思想是能夠對小尺寸對象不做 2 次內存分配. 解決方案是在 warp 對象裡預留一小塊空間安置小對象用. 基類和 warp 類就是這樣設計的. 代碼:-------------------------------------------------------------------------------- class var; // 基類是一個為空的東西 class var_null { public: typedef int var_type; enum { type='null' }; // 類型識別用, 每種類型用一個整數表示 var_null() {} virtual ~var_null() {} void *operator new ( size_t size , var *p); void operator delete (void *p, var *v) {} void *operator new ( size_t size) { return ::operator new(size); } void operator delete (void *p) { ::operator delete(p); } protected: virtual void clone(var *p) const { new(p)var_null; } void copy_to(var *p) const; bool is_type(var_type type) const { return get_type()==type; } virtual var_type get_type() const { return type; } private: virtual void do_copy_to(var_null &des) const {} friend class var; }; // 給出一個 null 是空對象 extern var_null null;


// warp 類 class var { public: var() {} ~var() {} var(const var &init) { init.clone(this); } var(const var_null &init) { init.clone(this); } const var& operator=(const var &src) { src.copy_to(this); return *this; } const var& operator=(const var_null &src) { src.copy_to(this); return *this; } bool is(var_null::var_type type) const { return data.obj.is_type(type); } bool is_null() const { return data.obj.is_type(var_null::type); } var_null::var_type get_type() const { return data.obj.get_type(); } protected: void clone(var *p) const { data.obj.clone(p); } void copy_to(var *p) const { data.obj.copy_to(p); } public: strUCt var_data { var_null obj; int uninitialized[3]; //存放小對象的空間 }; private: var_data data; friend class var_null; }; inline void var_null::copy_to(var *p) const { if (!p->is(get_type())) { p->data.obj.~var_null(); clone(p); } else do_copy_to(p->data.obj); } inline void * var_null::operator new ( size_t size , var *p) { assert(size<=sizeof(var::var_data)); return &(p->data.obj); } -------------------------------------------------------------------------------- 注意 var (warp) 類裡面沒有放 var_null 的指針, 而是放了一個 var_null 對象的實例. 而且在後面留了一小段空間. 這是這個優化方案的核心. var 在構造的時候同時構造了一個 var_null, 但是, 當我們再賦值的時候, 如果想賦的是一個 var_null 的派生類對象, var_null 的 copy_to 會檢查出來, 並且把原來這個地方的對象 析構掉(主動調用析構函數) 但是由於空間是 var 構造的時候就給出的, 所以不需要 釋放內存, 然後用 clone 在原地生成一個新的對象. 這裡在原地構造新對象是用重載 一個特殊版本的 new 實現的, 看 var_null 的 operator new , 它接受一個 var 指針, 然後計算出原來放 var_null 的位置, 直接返回. 這樣, 原來放 var_null 對象的位置, 就放了一個新的 var_null 派生物. 由於 var_nul 的析構函數是虛的, 這個新對象的 析構函數指針位置和原來的相同, 所以 var 在析構的時候, 無論這個位置放的什麼 都會正常的析構掉. 現在,由 var 管理的小對象就不需要 2 次內存分配了. 但是 var 裡預留的空間有限, 對於大對象, 我們依然需要保存對象指針. 為小對象, 和大對象, 我做了兩個不同的 template. 代碼:-------------------------------------------------------------------------------- // 直接放值的: template class _var_direct_value : public var_null { public: enum { type=type_id }; _var_direct_value() {} _var_direct_value(T d) : data(d) {} operator T() { return data; } protected: T data; private: var_type get_type() const { return type; } void do_copy_to(var_null &p) const { ((_var_direct_value &)p).data=data; } void clone(var *p) const { new(p) _var_direct_value(data); }

}; // 現在我們可以方便的讓 var_int 可以存放一個 int typedef _var_direct_value var_int; // 放對象指針的: template class _var_pointer : public var_null { public: enum { type=type_id }; _var_pointer() : data(new T) {} _var_pointer(const T &init) : data(new T(init)) {} _var_pointer(const _var_pointer &init) : data(new T(init.data)) {} _var_pointer(const var &init) { init.clone(this); } ~_var_pointer() { delete data; } operator T() { return *data; } const _var_pointer& operator=(const _var_pointer &v) { if (&v!=this) { delete data; data=new T(v.data); } return *this; } protected: T *data; private: var_type get_type() const { return type_id; } void do_copy_to(var_null &p) const { _var_pointer &v=(_var_pointer &)p; *(((_var_pointer &)p).data)=*data; } void clone(var *p) const { new(p) _var_pointer(*data); } }; -------------------------------------------------------------------------------- 看到這裡已經累了嗎? 可是還沒有完 (雖然看起來問題都解決了) 我們可以實現的更完美一些 :) 如果讓用戶來決定什麼時候該使用那個 template 實在是難為他們, 因為 需要計算 var 裡的那個空間到底放不放的下想放的東西. 如果更改 var 裡預留空間大小, 還會涉及到代碼的改變. 所以我使用了一個 template 的技巧來完成template 的自動選擇 代碼:-------------------------------------------------------------------------------- te



所以我使用了一個 template 的技巧來完成template 的自動選擇 代碼:-------------------------------------------------------------------------------- te



Copyright © Linux教程網 All Rights Reserved