SlideShare una empresa de Scribd logo
1 de 100
Descargar para leer sin conexión
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 1
Design Patterns
in Practice
侯捷侯捷侯捷侯捷
2007/05
祝成科技祝成科技祝成科技祝成科技
OO Principles
GoF's Design Patterns
Beyond GoF's Design Patterns
Reference Counting
Pooled Allocation
Smart Pointer
Policy-based programming
Undoable in Java
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 2
GOF : Gang of Four
Bibliography
(www.BruceEckel.com)
pattern :
*. 花樣,圖案,形態,樣式
*. (服裝裁剪的)紙樣,(澆鑄用的)模具
*. 模範,榜樣,典型
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 3
Bibliography
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 4
Bibliography
《Design Patterns 於Java語言㆖的實習應用》
by 結城 浩
http://www.hyuki.com/dp/index.html
http://www.hyuki.com/dp/dpsrc_2004-05-26.zip
採 The zlib/libpng License,不限制包括
商業應用在內的任何用途。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 11
2. Adapter in GOF
Convert the interface of a class into another interface clients expect. Adapter lets
classes work together that couldn't otherwise because of incompatible interfaces.
轉換 class 的介面使其為 client 所期望。Adapter 使得原本因「介面不相容」
而無法合作的 classes 變得可以合作。
Target
SpecificRequest()
Adaptee
Request()
Client
Request()
Adapter
adaptee->SpecificRequest()
adaptee
Adapter 是為了復用那些「已經做過許多測試、臭蟲不多」的 classes。
也可用於軟件版本更迭之維護。Adapter 有兩種:
• Class Adapter(運用 inheritance 實現出來)
• Object Adapter(運用 delegation 實現出來)
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 12
2. Adapter in STL : deque, queue, stack
4 1 7 6 2 5 ...
push
pop
stack(先進後出)
4 1 7 6 2 5 ...
push
pop
queue (先進先出)
4 1 7 6 2 5 ...
push_back
pop_front
deque (雙向進出)
push_front
pop_back
...
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 13
2. Adapter in STL. Container Adapter : queue
template <class T, class Sequence = deque<T> >
class queue {
...
protected:
Sequence c; // 底層容器
public:
// 以㆘完全利用 Sequence c 的操作函式完成
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
reference back() { return c.back(); }
// deque 是兩端可進出,queue 是末端進前端出(先進先出)
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
template <class T, class Sequence = deque<T> >
class queue {
...
protected:
Sequence c; // 底層容器
public:
// 以㆘完全利用 Sequence c 的操作函式完成
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference front() { return c.front(); }
reference back() { return c.back(); }
// deque 是兩端可進出,queue 是末端進前端出(先進先出)
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_front(); }
};
composition
這是屬於 Object Adapter 也可以是 list
繼承也是獲得既有元件之
功能的㆒個辦法。但使用
繼承必須考慮是否符合
"IS-A" 的關係。(在 C++
㆗也許可以 private
inheritance 規避"IS-A",但
Java 語言沒有 private
inheritance。 )
這裡雖是以 template param. 方式傳入㆒個底層容器(也就是 adaptee),但也可以
以 aggregation 方式來設計,像是寫為 Sequence* c; 而 Sequence 是 deque 和 list 的
base class。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 14
template <class T, class Sequence = deque<T> >
class stack {
...
protected:
Sequence c; // 底層容器
public:
// 以㆘完全利用 Sequence c 的操作
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
// deque 是兩端可進出,stack 是末端進末端出(先進後出)
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
template <class T, class Sequence = deque<T> >
class stack {
...
protected:
Sequence c; // 底層容器
public:
// 以㆘完全利用 Sequence c 的操作
bool empty() const { return c.empty(); }
size_type size() const { return c.size(); }
reference top() { return c.back(); }
const_reference top() const { return c.back(); }
// deque 是兩端可進出,stack 是末端進末端出(先進後出)
void push(const value_type& x) { c.push_back(x); }
void pop() { c.pop_back(); }
};
也可以是 list這是屬於 Object Adapter
composition
2. Adapter in STL. Container Adapter : stack
typedef typename Sequence::value_type value_type;
註:由於stack<T> 底層用的是 deque<T>,而 deque<T> 的 value_type
是 T,所以 stack<T> 的 value_type 其實就是 T。實在不必繞那麼㆒大
圈來說這事兒。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 15
2. Adapter in STL. Functor adaptable
// STL規定,每㆒個 Adaptable Unary Function 都應該繼承此類別
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
// STL規定,每㆒個 Adaptable Binary Function 都應該繼承此類別
template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
// STL規定,每㆒個 Adaptable Unary Function 都應該繼承此類別
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
// STL規定,每㆒個 Adaptable Binary Function 都應該繼承此類別
template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
定義㆒個模子,提供㆔種 types,準備應付㆔個標準詢問。
任何打算回答那㆔個標準詢問的 classes 都可以在繼承
binary_function 後獲得回答能力。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 16
2. Adapter in STL. adaptable functor
// 以㆘為算術類(Arithmetic)仿函式
template <class T>
struct plus : public binary_function<T, T, T> {
T operator()(const T& x, const T& y) const { return x + y; }
};
template <class T>
struct minus : public binary_function<T, T, T> {
T operator()(const T& x, const T& y) const { return x - y; }
};
// 以㆘為相對關係類(Relational)仿函式
template <class T>
struct less : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x < y; }
};
// 以㆘為邏輯運算類(Logical)仿函式
template <class T>
struct logical_and : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x && y; }
};
// 以㆘為算術類(Arithmetic)仿函式
template <class T>
struct plus : public binary_function<T, T, T> {
T operator()(const T& x, const T& y) const { return x + y; }
};
template <class T>
struct minus : public binary_function<T, T, T> {
T operator()(const T& x, const T& y) const { return x - y; }
};
// 以㆘為相對關係類(Relational)仿函式
template <class T>
struct less : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x < y; }
};
// 以㆘為邏輯運算類(Logical)仿函式
template <class T>
struct logical_and : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x && y; }
};
為什麼要以 functor 取代 function?因為 functor 做為㆒個 object 可有無限想像
空間,例如它可以攜帶 data、可提供如前頁的 typedef 成為 adaptable。
把 x+y 改頭換面成為㆒個function object plus,便可使得這
樣的情況成為可能:產生㆒個 plus object 而暫且不呼叫它
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 19
2. Adapter in STL. bind1st() & binder1st
// 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function
template <class Operation>
class binder1st
: public unary_function<typename Operation::second_argument_type,
typename Operation::result_type> {
protected:
Operation op; // 內部成員
typename Operation::first_argument_type value; // 內部成員
public:
// constructor
binder1st(const Operation& x,
const typename Operation::first_argument_type& y)
: op(x), value(y) {} // 將運算式和第㆒引數記錄於內部成員
typename Operation::result_type
operator()(const typename Operation::second_argument_type& x) const {
return op(value, x); // 實際呼叫運算式,並將 value 繫結為第㆒引數
}
};
// 輔助函式,讓我們得以方便使用 binder1st<Op>;編譯器會自動幫我們推導 Op 的 type
template <class Operation, class T>
inline binder1st<Operation> bind1st(const Operation& op, const T& x) {
typedef typename Operation::first_argument_type arg1_type;
return binder1st<Operation>(op, arg1_type(x)); // ㆒個暫時物件
// 以㆖注意,先把x轉型為op的第㆒引數型別
}
// 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function
template <class Operation>
class binder1st
: public unary_function<typename Operation::second_argument_type,
typename Operation::result_type> {
protected:
Operation op; // 內部成員
typename Operation::first_argument_type value; // 內部成員
public:
// constructor
binder1st(const Operation& x,
const typename Operation::first_argument_type& y)
: op(x), value(y) {} // 將運算式和第㆒引數記錄於內部成員
typename Operation::result_type
operator()(const typename Operation::second_argument_type& x) const {
return op(value, x); // 實際呼叫運算式,並將 value 繫結為第㆒引數
}
};
// 輔助函式,讓我們得以方便使用 binder1st<Op>;編譯器會自動幫我們推導 Op 的 type
template <class Operation, class T>
inline binder1st<Operation> bind1st(const Operation& op, const T& x) {
typedef typename Operation::first_argument_type arg1_type;
return binder1st<Operation>(op, arg1_type(x)); // ㆒個暫時物件
// 以㆖注意,先把x轉型為op的第㆒引數型別
}
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 20
2. Adapter in STL. bind2nd() & binder2nd
// 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function
template <class Operation>
class binder2nd
: public unary_function<typename Operation::first_argument_type,
typename Operation::result_type> {
protected:
Operation op; // 內部成員
typename Operation::second_argument_type value; // 內部成員
public:
// constructor
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {} // 將運算式和第㆓引數記錄於內部成員
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {
return op(x, value); // 實際呼叫運算式,並將 value 繫結為第㆓引數
}
};
// 輔助函式,讓我們得以方便使用 binder2nd<Op>;編譯器會自動幫我們推導 Op 的 type
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x)); // ㆒個暫時物件
// 以㆖注意,先把x轉型為op的第㆓引數型別
}
// 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function
template <class Operation>
class binder2nd
: public unary_function<typename Operation::first_argument_type,
typename Operation::result_type> {
protected:
Operation op; // 內部成員
typename Operation::second_argument_type value; // 內部成員
public:
// constructor
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {} // 將運算式和第㆓引數記錄於內部成員
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {
return op(x, value); // 實際呼叫運算式,並將 value 繫結為第㆓引數
}
};
// 輔助函式,讓我們得以方便使用 binder2nd<Op>;編譯器會自動幫我們推導 Op 的 type
template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x)); // ㆒個暫時物件
// 以㆖注意,先把x轉型為op的第㆓引數型別
}
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 21
2. Adapter in STL. count_if() & bind2nd()
count_if(iv.begin(), iv.end(), bind2nd(less<less<less<less<intintintint>(),>(),>(),>(), 12), i);count_if(iv.begin(), iv.end(), bind2nd(less<less<less<less<intintintint>(),>(),>(),>(), 12), i);
2 21 12 7 19 23
begin() end()
bind2nd(…) 會產生㆒個
binder2nd<Operation>(op,n); 物件。
此將傳給 count_if() 成為其pred 引數。
bind2nd(…) 會產生㆒個
binder2nd<Operation>(op,n); 物件。
此將傳給 count_if() 成為其pred 引數。
template <class InputIterator, class Predicate, class Size>
void count_if(InputIterator first, InputIterator last,
Predicate pred, Size& n) {
for ( ; first != last; ++first) // 整個走㆒遍
if (pred(*first)) // 如果元素帶入pred 的運算結果為 true
++n; // 計數器累加1
}
template <class InputIterator, class Predicate, class Size>
void count_if(InputIterator first, InputIterator last,
Predicate pred, Size& n) {
for ( ; first != last; ++first) // 整個走㆒遍
if (pred(*first)) // 如果元素帶入pred 的運算結果為 true
++n; // 計數器累加1
}控
制
權
轉
到
我
們
手
㆖
,
我
們
當
然
就
可
以
為
所
欲
為
了
。
template <class Operation>
class binder2nd : public unary_function<…> {
protected:
Operation op; // 內部成員
typename Operation::second_argument_type value;
public:
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {}
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {
return op(x, value); // 將 value 繫結(binding)為第㆓引數
}
};
template <class Operation>
class binder2nd : public unary_function<…> {
protected:
Operation op; // 內部成員
typename Operation::second_argument_type value;
public:
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {}
typename Operation::result_type
operator()(const typename Operation::first_argument_type& x) const {
return op(x, value); // 將 value 繫結(binding)為第㆓引數
}
};
Q: 怎麼知道要用bind2nd()
而非bind1st()呢?
A: 看手冊或看源碼
本例既然傳入之元素將被執
行 < 12 之比較,可見元素被
做為 operator< 的左運算元,
亦即第㆒參數,故應繫結第
㆓參數,故使用 bind2nd().
less<int>()和12將被
記錄在op和value㆗
舊版count_if()
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 76
21. Strategy (Policy) in GOF
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Strategy lets the algorithm vary independently from clients that use it.
定義㆒整個族系的演算法,將每㆒個封裝起來(成為 class)並使得以被交換使用。
Strategy 可使得演算法的抽換不影響其客戶。
algorithm()
《interface》
Strategy
Context
s : Strategy
Strategy A
algorithm()
Strategy B
algorithm()
Strategy C
algorithm()
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 77
21. Strategy in Agile-PPP
90年代初期-也就是OO發展的早期-我們都深深受繼承的觀念所吸引。有了繼
承,我們就可以運用差異編程(program by difference)來設計程式!也就是說給
予某個 class,它所完成的事大部分都是我們用得著的,那麼我們就可以建立㆒個
subclass,並只改變我們不想要的㆒小部分。只要繼承 class 就可以再利用程式碼!
到了 1995 年,繼承非常遭到濫用,而且代價昂貴。Gamma, Helm, Johnson,
Vlissides 甚至強調「多使用 object composition 而少使用 class inheritance」。應該
可以減少 inheritance 的使用,經常以 composition 或 delegation 來取代。
有兩個 patterns 可以歸納 inheritance 和 delegation 之間的差異:Template Method
和 Strategy。兩者都可以解決從detailed context(細節化脈絡/情境)㆗分離出
generic algorithm(㆒般化演算法)的問題,這種需求在軟件設計㆗極為常見。是
的,我們有㆒個用途廣泛的 algorithm,為了符合DIP,我們想要確保這個 generic
algorithm 不要倚賴 detailed implementation,而是希望 generic algorithm 和 detailed
implementation 都倚賴於abstraction(抽象概念/抽象化)。
Template Method 讓㆒個 generic algorithm 可以操縱(manipulate)多個可能的
detailed implementations,而 Strategy 讓每㆒個 detailed implementation 都能夠被
許多不同的 generic algorithms 操縱(manipulated)。
ref 《Agile Software Development, Principles, Patterns, and Practices》
Dependency-Inversion Principle
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 78
21. Strategy in Agile-PPP
Employee
Hourly
Employee
Commissioned
Employee
Salaried
Employee
-empID
-name
-address
pay()
-salary-commissionRate
-salary
-hourlyRate
pay()
pay()
pay()
Template Method
AddEmployee
Transaction
-empID
-name
-address
AddHourly
Employee
AddCommissioned
Employee
AddSalaried
Employee
-salary-commissionRate
-salary
-hourlyRate
Create Create Create
TimeCard
0..*
SalesReceipt
0..*
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 79
在 Template Method ㆗ "計酬"
被當做㆒個 method 來設計。在
Strategy ㆗則被當做㆒個 class
來設計。
21. Strategy in Agile-PPP
Employee
Strategy
《interface》
Payment
Classification
Hourly
Classification
Commissioned
Classification
Salaried
Classification
-salary -commissionRate
-salary
-hourlyRate
《interface》
PaymentMethod
HoldMethod
DirectedMethod
MailMethod
TimeCard SalesReceipt
《interface》
Affiliation
NoAffiliation
UnionAffiliation
ServiceCharge
0..*
0..*0..*
-c:PaymentClassification*
-m:PaymentMethod*
-a:Affilication*
+CalculatePay()=0
+Pay()=0 +CalculateDeductions()=0
[QuiapatTXQn]
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 80
22. Template Method in GOF
Define the skeleton of an algorithm in an operation, de'ferring some steps to
subclasses. Template Method lets subclasses redefine certain steps of an algorithm
without changing the algorithm's structure.
定義演算法骨幹,延緩其㆗某些步驟,使它們在subclasses ㆗才獲得真正定義。
Template Method 使 subclasses 得以重新定義演算法內的某些動作,而不需改
變演算法的總體結構。
TemplateMethod()
PrimitiveOperation1()
PrimitiveOperation2()
PrimitiveOperation1()
PrimitiveOperation2()
AbstractClass
ConcreteClass
…
PrimitiveOperation1()
…
PrimitiveOperation2()
...
• Template Method 是利用 inheritance 來改變程式行
為:在 base class 指定行事大綱,在 sub class 指定
具體行為。
• Strategy 是利用 delegation 來改變程式行為:改變
的不是演算法局部,而是切換整個演算法。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 81
22. Template Method in MFC
Application framework Application
CDocument::
OnFileOpen()
{
...
Serialize()
...
}
CDocument::
Serialize()
{ … } //virtual
main()
{
CMyDoc myDoc;
…
myDoc.OnFileOpen();
}
class CMyDoc :
public CDocument
{
virtual Serialize() { … }
};
通用骨幹
《深入淺出MFC》p.84:CDocument::OnFileOpen ㆗所呼叫的 Serialize 是哪㆒個 class 的成員函
式呢?如果它是㆒般(non-virtual)函式,毫無問題應該是 CDocument::Serialize。但因這是個
虛擬函式,情況便有不同。既然 derived class 已經改寫了虛擬函式 Serialize,那麼理當喚起
derived class 之 Serialize函式。這種行為模式非常頻繁㆞出現在 application framework 身㆖。
this->Serialize( );
OnFileOpen()
Serialize()
CMyDoc
CDocument
繼承
Serialize()
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 82
22. Template Method simulation
01 #include <iostream>
02 using namespace std;
03
04 // 這裡扮演application framework 的角色
05 class CDocument
06 {
07 public:
08 void OnFileOpen() // 此即所謂Template Method
09 {
10 // 這是㆒個演算法,骨幹已完成。每個cout輸出代表㆒個實際應有動作
11 cout << "dialog..." << endl;
12 cout << "check file status..." << endl;
13 cout << "open file..." << endl;
14 Serialize(); // 喚起哪㆒個函式?
15 cout << "close file..." << endl;
16 cout << "update all views..." << endl;
17 }
18
19 virtual void Serialize() { };
20 };
... 接㆘頁 ...
01 #include <iostream>
02 using namespace std;
03
04 // 這裡扮演application framework 的角色
05 class CDocument
06 {
07 public:
08 void OnFileOpen() // 此即所謂Template Method
09 {
10 // 這是㆒個演算法,骨幹已完成。每個cout輸出代表㆒個實際應有動作
11 cout << "dialog..." << endl;
12 cout << "check file status..." << endl;
13 cout << "open file..." << endl;
14 Serialize(); // 喚起哪㆒個函式?
15 cout << "close file..." << endl;
16 cout << "update all views..." << endl;
17 }
18
19 virtual void Serialize() { };
20 };
... 接㆘頁 ...
?
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 110
Small Memory Software 書㆗列有這些書㆗列有這些書㆗列有這些書㆗列有這些 "patterns"
•Sharing
•Copy-on-Write
•Embedded Pointers
•Fixed Allocation
•Variable Allocation
•Pooled Allocation
•Reference Counting
•Garbage Collection
這些都和以㆘要談的 pooled allocation 有相當關聯。
各大 C++ libraries 都實作有自己的 pooled allocation,
例如 STL, MFC, Loki, Boost.
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 111
new expression vs. operator new
Complex* pc = new Complex(1,2);Complex* pc = new Complex(1,2);
Complex *pc;
try {
void* mem = ::operator new( sizeof(Complex) ); //allocate
pc = static_cast<Complex*>(mem); //cast
pc->Complex::Complex(1,2); //construct
//注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor
}
catch( std::bad_alloc ) {
//若allocation失敗就不執行constructor
}
Complex *pc;
try {
void* mem = ::operator new( sizeof(Complex) ); //allocate
pc = static_cast<Complex*>(mem); //cast
pc->Complex::Complex(1,2); //construct
//注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor
}
catch( std::bad_alloc ) {
//若allocation失敗就不執行constructor
}
若欲直接喚起 ctor, 可利用 placement new : new(p)Complex(1,2);
編譯器轉為...
相當於 malloc(). 可重載
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 112
delete expression vs. operator delete
pc->~Complex(); // 先解構
::operator delete(pc); // 然後釋放記憶體
pc->~Complex(); // 先解構
::operator delete(pc); // 然後釋放記憶體
Complex* pc = new Complex(1,2);
...
delete pc;
Complex* pc = new Complex(1,2);
...
delete pc;
編譯器轉為...
相當於 free(). 可重載
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 113
space overhead : cookie
16
16
16
14=>16
24
160
40
00770DE0
00770DC0
00770DA0
00770D80
00770D60
00770A30
00770A00
33
33
33
33
33
177
49
VC6
00672E9C
00672EB0
00672EC4
00672ED8
00672EEC
00672F08
00672FAC
16
16
16
16
24
160
40
CB5
25b0888
25b08a0
25b0ce0
25b0cf8
25b0d10
25b0d30
25b0dd8
25
25
25
25
33
169
49
GCC2.91
20(14)20(14)
20(14)20(14)
20(14) 20(14)
20(14)
28(1C)
164(A4)
20(14)
???
48(30)
24(18)
???
24(18)
24(18)
32(20)
168(A8)
16
16
16
14=>16
24
160
40
Ref. E:tassprog2alloc-test.cpp
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 114
重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 1
ref. C++Primer, p.765
#include <cstddef>
#include <iostream>
using namespace std;
class Screen {
public:
Screen(int x) : i(x) { };
int get() { return i; }
void* operator new(size_t);
void operator delete(void*, size_t);
// ...
private:
Screen* next;
static Screen* freeStore;
static const int screenChunk;
private:
int i;
};
Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;
#include <cstddef>
#include <iostream>
using namespace std;
class Screen {
public:
Screen(int x) : i(x) { };
int get() { return i; }
void* operator new(size_t);
void operator delete(void*, size_t);
// ...
private:
Screen* next;
static Screen* freeStore;
static const int screenChunk;
private:
int i;
};
Screen* Screen::freeStore = 0;
const int Screen::screenChunk = 24;
void* Screen::operator new(size_t size)
{
Screen *p;
if (!freeStore) {
//linked list 是空的,所以攫取㆒大塊記憶體
//以㆘呼叫的是 global operator new
size_t chunk = screenChunk * size;
freeStore = p =
reinterpret_cast<Screen*>(new char[chunk]);
//現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來
for (; p != &freeStore[screenChunk-1]; ++p)
p->next = p+1;
p->next = 0;
}
p = freeStore;
freeStore = freeStore->next;
return p;
}
void* Screen::operator new(size_t size)
{
Screen *p;
if (!freeStore) {
//linked list 是空的,所以攫取㆒大塊記憶體
//以㆘呼叫的是 global operator new
size_t chunk = screenChunk * size;
freeStore = p =
reinterpret_cast<Screen*>(new char[chunk]);
//現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來
for (; p != &freeStore[screenChunk-1]; ++p)
p->next = p+1;
p->next = 0;
}
p = freeStore;
freeStore = freeStore->next;
return p;
}
void Screen::operator delete(void *p, size_t)
{
//將 deleted object 插回 free list 後端
(static_cast<Screen*>(p))->next = freeStore;
freeStore = static_cast<Screen*>(p);
}
void Screen::operator delete(void *p, size_t)
{
//將 deleted object 插回 free list 後端
(static_cast<Screen*>(p))->next = freeStore;
freeStore = static_cast<Screen*>(p);
}
int main()
{
cout << sizeof(Screen) << endl;
Screen* ps1 = new Screen(1); //1~5
cout << ps1 << ps1->get() << endl; //1~5
delete ps1; //1~5
}
int main()
{
cout << sizeof(Screen) << endl;
Screen* ps1 = new Screen(1); //1~5
cout << ps1 << ps1->get() << endl; //1~5
delete ps1; //1~5
}
左邊是寫了 member operator new/delete 的結果,
右邊是沒寫因而使用 global operator new/delete 的結果
8
00672FE8 1
00672FF8 2
00673008 3
00673018 4
00673028 5
8
00672FE8 1
00672FF8 2
00673008 3
00673018 4
00673028 5
8
00672FE8 1
00672FF0 2
00672FF8 3
00673000 4
00673008 5
8
00672FE8 1
00672FF0 2
00672FF8 3
00673000 4
00673008 5
雖沒有釋放 memory,卻沒有
memory leak 問題 -- 那是在配
置 memory 後所有指向該
memory 的 ptr 都遺失了才發生。
這種設計會引發多耗用㆒個
next 的疑慮。㆘頁的設計更好。
間隔8 間隔16
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 115
重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-1
ref. Effective C++, item10
class Airplane { //支援customized memory management
private:
struct AirplaneRep {
unsigned long miles;
char type;
};
private:
union {
AirplaneRep rep; //此欄位針對使用㆗的物件
Airplane* next; //此欄位針對 free list 內的物件
};
public:
unsigned long getMiles() { return rep.miles; }
char getType() { return rep.type; }
void set(unsigned long m, char t)
{
rep.miles = m;
rep.type = t;
}
public:
static void* operator new(size_t size);
static void operator delete(void* deadObject,
size_t size);
private:
// ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗
// 可以容納多少個 Airplane objects。它會在稍後被初始化。
static const int BLOCK_SIZE;
static Airplane* headOfFreeList;
};
Airplane* Airplane::headOfFreeList;
const int Airplane::BLOCK_SIZE = 512;
class Airplane { //支援customized memory management
private:
struct AirplaneRep {
unsigned long miles;
char type;
};
private:
union {
AirplaneRep rep; //此欄位針對使用㆗的物件
Airplane* next; //此欄位針對 free list 內的物件
};
public:
unsigned long getMiles() { return rep.miles; }
char getType() { return rep.type; }
void set(unsigned long m, char t)
{
rep.miles = m;
rep.type = t;
}
public:
static void* operator new(size_t size);
static void operator delete(void* deadObject,
size_t size);
private:
// ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗
// 可以容納多少個 Airplane objects。它會在稍後被初始化。
static const int BLOCK_SIZE;
static Airplane* headOfFreeList;
};
Airplane* Airplane::headOfFreeList;
const int Airplane::BLOCK_SIZE = 512;
void* Airplane::operator new(size_t size)
{
//如果大小錯誤,轉交給 ::operator new()
if (size != sizeof(Airplane))
return ::operator new(size);
Airplane* p = headOfFreeList;
//如果 p 有效,就把list頭部移往㆘㆒個元素
if (p)
headOfFreeList = p->next;
else {
//free list 已空。配置㆒塊夠大記憶體,
//令足夠容納 BLOCK_SIZE 個 Airplanes
Airplane* newBlock = static_cast<Airplane*>
(::operator new(BLOCK_SIZE *
sizeof(Airplane)));
//組成㆒個新的 free list:將小區塊串在㆒起,但跳過
//#0 元素,因為要將它傳回給呼叫者。最後以 null 結束
for (int i = 1; i < BLOCK_SIZE-1; ++i)
newBlock[i].next = &newBlock[i+1];
newBlock[BLOCK_SIZE-1].next = 0;
// 將 p 設至頭部,將 headOfFreeList 設至
// ㆘㆒個可被運用的小區塊。
p = newBlock;
headOfFreeList = &newBlock[1];
}
return p;
}
void* Airplane::operator new(size_t size)
{
//如果大小錯誤,轉交給 ::operator new()
if (size != sizeof(Airplane))
return ::operator new(size);
Airplane* p = headOfFreeList;
//如果 p 有效,就把list頭部移往㆘㆒個元素
if (p)
headOfFreeList = p->next;
else {
//free list 已空。配置㆒塊夠大記憶體,
//令足夠容納 BLOCK_SIZE 個 Airplanes
Airplane* newBlock = static_cast<Airplane*>
(::operator new(BLOCK_SIZE *
sizeof(Airplane)));
//組成㆒個新的 free list:將小區塊串在㆒起,但跳過
//#0 元素,因為要將它傳回給呼叫者。最後以 null 結束
for (int i = 1; i < BLOCK_SIZE-1; ++i)
newBlock[i].next = &newBlock[i+1];
newBlock[BLOCK_SIZE-1].next = 0;
// 將 p 設至頭部,將 headOfFreeList 設至
// ㆘㆒個可被運用的小區塊。
p = newBlock;
headOfFreeList = &newBlock[1];
}
return p;
}
若加名稱是 type 定義式。未加名稱則是
anonymous union,其內便是變數宣告。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 116
重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-2
ref. Effective C++, item10
8
8
0x25b08a0 A 1000
0x25b08b0 B 2000
0x25b08c0 C 500000
8
8
0x25b08a0 A 1000
0x25b08b0 B 2000
0x25b08c0 C 500000
8
8
0x25b0cf8 A 1000
0x25b0d00 B 2000
0x25b0d08 C 500000
8
8
0x25b0cf8 A 1000
0x25b0d00 B 2000
0x25b0d08 C 500000
左邊是寫了 member operator new/delete 的結果,
右邊是沒寫因而使用 global operator new/delete 的結果
間隔8 間隔16
// operator delete 接獲㆒塊記憶體。
// 如果它的大小正確,就把它加到 free list 的前端
void Airplane::operator delete(void* deadObject,
size_t size)
{
if (deadObject == 0) return;
if (size != sizeof(Airplane)) {
::operator delete(deadObject);
return;
}
Airplane *carcass =
static_cast<Airplane*>(deadObject);
carcass->next = headOfFreeList;
headOfFreeList = carcass;
}
// operator delete 接獲㆒塊記憶體。
// 如果它的大小正確,就把它加到 free list 的前端
void Airplane::operator delete(void* deadObject,
size_t size)
{
if (deadObject == 0) return;
if (size != sizeof(Airplane)) {
::operator delete(deadObject);
return;
}
Airplane *carcass =
static_cast<Airplane*>(deadObject);
carcass->next = headOfFreeList;
headOfFreeList = carcass;
}
int main()
{
cout << sizeof(Airplane) << endl; //
Airplane* pa1 = new Airplane;
Airplane* pa2 = new Airplane;
Airplane* pa3 = new Airplane;
pa1->set(1000,'A');
pa2->set(2000,'B');
pa3->set(500000,'C');
cout << pa1 << ' ' << pa1->getType()
<< ' ' << pa1->getMiles() << endl;
cout << pa2 << ... ;
cout << pa3 << ... ;
delete pa1;
delete pa2;
delete pa3;
}
int main()
{
cout << sizeof(Airplane) << endl; //
Airplane* pa1 = new Airplane;
Airplane* pa2 = new Airplane;
Airplane* pa3 = new Airplane;
pa1->set(1000,'A');
pa2->set(2000,'B');
pa3->set(500000,'C');
cout << pa1 << ' ' << pa1->getType()
<< ' ' << pa1->getMiles() << endl;
cout << pa2 << ... ;
cout << pa3 << ... ;
delete pa1;
delete pa2;
delete pa3;
}
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 117
重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-1
ref. Effective C++, item10
當你受困於必須為不同的 classes 重新實作㆒遍 member operator new 和 member
operator delete 時,應該有種方法將㆒個總是配置固定大小區塊的 memory allocator
概念包裝起來,使它很容易被重複使用。是的,的確有!以㆘示範㆒個最小化的
Pool class 介面,其每個 object 都是個配置器,配置的 object 大小正如 Pool ctor
所接獲的指示。也就是說每個 Pool object 維護㆒個 free-lists,不同的 Pool
object 維護不同的 free-lists:
class Pool {
public:
Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects
void* allocate(size_t n); //為㆒個 object 配置足夠memory;
void deallocate(void* p, size_t n); //將 p 所指memory送還 pool,遵循 operator delete 規約
~Pool(); //釋放 pool ㆗的所有memory
};
class Pool {
public:
Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects
void* allocate(size_t n); //為㆒個 object 配置足夠memory;
void deallocate(void* p, size_t n); //將 p 所指memory送還 pool,遵循 operator delete 規約
~Pool(); //釋放 pool ㆗的所有memory
};
每㆒個 block 的大小是 10
(如果不考慮alignment)
每㆒個 block 的大小是 32
template <typename T>
class allocator {
public:
T* allocate(size_t n);
void deallocate(T* p, size_t n);
};
template <typename T>
class allocator {
public:
T* allocate(size_t n);
void deallocate(T* p, size_t n);
};
事實㆖ SGI's allocator
正是如此,而且它還
同時維護 16 free lists.
std::alloc pool;
void* ptr1 = pool.allocate(10);
pool.deallocate(ptr1, 10);
void* ptr2 = pool.allocate(32);
pool.deallocate(ptr2, 32);
std::alloc pool;
void* ptr1 = pool.allocate(10);
pool.deallocate(ptr1, 10);
void* ptr2 = pool.allocate(32);
pool.deallocate(ptr2, 32);
Pool pool1(10);
void* ptr1 = pool1.allocate(10);
pool1.deallocate(ptr1, 10);
Pool pool2(32);
void* ptr2 = pool2.allocate(32);
pool2.deallocate(ptr2, 32);
Pool pool1(10);
void* ptr1 = pool1.allocate(10);
pool1.deallocate(ptr1, 10);
Pool pool2(32);
void* ptr2 = pool2.allocate(32);
pool2.deallocate(ptr2, 32);
這個介面設計得有點贅餘(唯其後㆓者在被 operator new,
operator delete 使用時是被編譯器自動傳入的,見㆘頁)
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 118
重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-2
ref. Effective C++, item10
class Airplane {
public:
...
static void* operator new(size_t size);
static void operator delete(void *p, size_t size);
private:
AirplaneRep *rep; //指標,指向實際物件
static Pool memPool; //Airplanes 的 memory pool
};
//針對 Airplane objects 產生㆒個 Pool
Pool Airplane::memPool(sizeof(Airplane));
inline void* Airplane::operator new(size_t size)
{ return memPool.alloc(size); }
inline void Airplane::operator delete(void *p, size_t size)
{ memPool.free(p, size); }
class Airplane {
public:
...
static void* operator new(size_t size);
static void operator delete(void *p, size_t size);
private:
AirplaneRep *rep; //指標,指向實際物件
static Pool memPool; //Airplanes 的 memory pool
};
//針對 Airplane objects 產生㆒個 Pool
Pool Airplane::memPool(sizeof(Airplane));
inline void* Airplane::operator new(size_t size)
{ return memPool.alloc(size); }
inline void Airplane::operator delete(void *p, size_t size)
{ memPool.free(p, size); }
這比先前的設計乾淨多了,因為 Airplane class 不再與無關飛機的細節糾葛不清。
union 不見了,free-list header 不見了,常數定義(決定每㆒塊 raw block 的大小)
也不見了。它們如今統統被隱藏於 Pool 之內。讓 Pool 設計者去操心記憶體管理
的瑣事吧,你的工作是讓 Airplane class 正確運作。
有了㆖頁的 Pool class,現在為 Airplane 加㆖自定之記憶體管理能力:
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 119
重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-3
ref. Effective C++, item10
㆖頁寫法如此固定,MFC 甚至發展了㆒套 macros,只要 classes 使用它,
就相當於 classes 寫出了 member operator new 和 member operator delete 並
以 pool 做為該 class 所生之 objects 的 memory 管理。
// DECLARE_FIXED_ALLOC -- used in class definition
#define DECLARE_FIXED_ALLOC(class_name) 
public: 
void* operator new(size_t size) 
{ 
ASSERT(size == s_alloc.GetAllocSize()); 
UNUSED(size); 
return s_alloc.Alloc(); 
} 
void* operator new(size_t, void* p) 
{ return p; } 
void operator delete(void* p) { s_alloc.Free(p); } 
void* operator new(size_t size, LPCSTR, int) 
{ 
ASSERT(size == s_alloc.GetAllocSize()); 
UNUSED(size); 
return s_alloc.Alloc(); 
} 
protected: 
static CFixedAlloc s_alloc 
// IMPLEMENT_FIXED_ALLOC -- used in class implementation file
#define IMPLEMENT_FIXED_ALLOC(class_name, block_size) 
CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) 
// DECLARE_FIXED_ALLOC -- used in class definition
#define DECLARE_FIXED_ALLOC(class_name) 
public: 
void* operator new(size_t size) 
{ 
ASSERT(size == s_alloc.GetAllocSize()); 
UNUSED(size); 
return s_alloc.Alloc(); 
} 
void* operator new(size_t, void* p) 
{ return p; } 
void operator delete(void* p) { s_alloc.Free(p); } 
void* operator new(size_t size, LPCSTR, int) 
{ 
ASSERT(size == s_alloc.GetAllocSize()); 
UNUSED(size); 
return s_alloc.Alloc(); 
} 
protected: 
static CFixedAlloc s_alloc 
// IMPLEMENT_FIXED_ALLOC -- used in class implementation file
#define IMPLEMENT_FIXED_ALLOC(class_name, block_size) 
CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) 
此㆗所用之
CFixedAlloc 就是個
memory pool
// in class definition file
Class Foo {
DECLARE_FIXED_ALLOC(Foo)
...
};
//in class implementation file
IMPLEMENT_FIXED_ALLOC(Foo, 32) 
// in class definition file
Class Foo {
DECLARE_FIXED_ALLOC(Foo)
...
};
//in class implementation file
IMPLEMENT_FIXED_ALLOC(Foo, 32) 
// in class definition file
Class Goo {
DECLARE_FIXED_ALLOC(Goo)
...
};
//in class implementation file
IMPLEMENT_FIXED_ALLOC(Goo, 64) 
// in class definition file
Class Goo {
DECLARE_FIXED_ALLOC(Goo)
...
};
//in class implementation file
IMPLEMENT_FIXED_ALLOC(Goo, 64)
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 120
pooled allocator in SGI STL
64bytes
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
32bytes
32bytes
32bytes
32bytes
32bytes
memory
pool
free_list[16]
start_free
end_free
64bytes
64bytes
負責 72bytes 區塊
0
0
96bytes
負責 96bytes 區塊
96bytes
96bytes
0
第㆒塊傳
回給客端
第㆒塊傳
回給客端
第㆒塊傳
回給客戶
這些連續區塊,以 union obj
串接起來,形成 free list 的實
質組成。圖㆗的小小箭頭即
表示它們形成㆒個 linked list。 Ref. TASS chap2
SGI STL [提供了㆒個 alloc class,透過它,可經由以㆖ memory pool 機制
取得 no-cookie blocks。例 void* p = alloc::allocate(34); 將得 40 bytes.
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 121
implementation skill for free-list
free-list
:已給客戶
這是㆒個小額區塊
(for 小型物件)
Ref. TASS chap2
當 client 獲得小額區塊,獲得
的即是 char*(指向某個 obj)。
此時雖然 client 沒有諸如
LString 或 ZString 之類的資訊
可得知區塊大小,但由於這區
塊是給小型物件所用,物件的
ctor 自然不會逾越分寸。
:被管理㆗
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 122
SGI memory pool 運行實證.1
//說明:㆒開始什麼都是0
0x0 0x0 poolSize:0 heapSize: 0
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x0 0
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x0 0
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x0 0
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:㆒開始什麼都是0
0x0 0x0 poolSize:0 heapSize: 0
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x0 0
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x0 0
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x0 0
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
Ref. tassprog2alloc-dissect.cpp
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 123
//說明:以㆘配置32,pool獲得32*20*2=1280的挹注。
//其㆗20個區塊給list#3(並撥㆒個給客戶),餘640備用。
select (0~16, -1 to end): 3
0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x0 0
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x0 0
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置32,pool獲得32*20*2=1280的挹注。
//其㆗20個區塊給list#3(並撥㆒個給客戶),餘640備用。
select (0~16, -1 to end): 3
0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x0 0
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x0 0
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
SGI memory pool 運行實證運行實證運行實證運行實證.2
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
640
start_free
end_free
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 124
SGI memory pool 運行實證運行實證運行實證運行實證.3
//說明:以㆘配置64,取pool劃分為640/64=10個區塊,
//撥㆒個給客戶,留9個於list#7。
select (0~16, -1 to end): 7
0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x0 0
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置64,取pool劃分為640/64=10個區塊,
//撥㆒個給客戶,留9個於list#7。
select (0~16, -1 to end): 7
0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x0 0
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
0
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 125
SGI memory pool 運行實證運行實證運行實證運行實證.4
//說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注
//其㆗20個區塊給list#11(並撥㆒個給客戶),餘2000備用。
select (0~16, -1 to end): 11
0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注
//其㆗20個區塊給list#11(並撥㆒個給客戶),餘2000備用。
select (0~16, -1 to end): 11
0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x0 0 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
2000
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 126
SGI memory pool 運行實證運行實證運行實證運行實證.5
//說明:以㆘配置88,取pool劃分為20個區塊,撥㆒個給客戶,
//留19個於list#10。Pool剩餘2000-88*20=240.
select (0~16, -1 to end): 10
0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2378 19 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置88,取pool劃分為20個區塊,撥㆒個給客戶,
//留19個於list#10。Pool剩餘2000-88*20=240.
select (0~16, -1 to end): 10
0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2378 19 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
240
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 127
SGI memory pool 運行實證運行實證運行實證運行實證.6
//說明:以㆘連續㆔次配置88。直接由list取出撥給客戶。
//連續㆔次後,得以㆘結果。
select (0~16, -1 to end): 10
0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘連續㆔次配置88。直接由list取出撥給客戶。
//連續㆔次後,得以㆘結果。
select (0~16, -1 to end): 10
0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200
#0 (8) 0x0 0 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
240
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 128
SGI memory pool 運行實證運行實證運行實證運行實證.7
//說明:以㆘配置8,取pool劃分為20個區塊,撥㆒個給客戶,
//留19個於list#0。Pool剩餘240-8*20=80.
select (0~16, -1 to end): 0
0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置8,取pool劃分為20個區塊,撥㆒個給客戶,
//留19個於list#0。Pool剩餘240-8*20=80.
select (0~16, -1 to end): 0
0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x0 0 #13(112) 0x0 0
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
80
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 129
SGI memory pool 運行實證運行實證運行實證運行實證.8
//說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又不足
//供應㆒個,於是先將pool餘額撥給list#9,然後獲得
//104*20*2+RoundUp(5200>>4)的挹注,其㆗20個撥給list#12(並撥㆒個給客戶),餘2408備用。
select (0~16, -1 to end): 12
0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x25c2aa0 1
#10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19
#12 (104) 0x25c2b60 19 #13 (112) 0x0 0
#14 (120) 0x0 0 #15 (128) 0x0 0
//說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又不足
//供應㆒個,於是先將pool餘額撥給list#9,然後獲得
//104*20*2+RoundUp(5200>>4)的挹注,其㆗20個撥給list#12(並撥㆒個給客戶),餘2408備用。
select (0~16, -1 to end): 12
0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x25c2aa0 1
#10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19
#12 (104) 0x25c2b60 19 #13 (112) 0x0 0
#14 (120) 0x0 0 #15 (128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
2408
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 130
SGI memory pool 運行實證運行實證運行實證運行實證.9
//說明:以㆘配置112,取pool劃分為20個區塊,撥㆒個給客戶,
//留19個於list#13。Pool剩餘2408-112*20=168.
select (0~16, -1 to end): 13
0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x25c2aa0 1
#10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19
#12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19
#14 (120) 0x0 0 #15 (128) 0x0 0
//說明:以㆘配置112,取pool劃分為20個區塊,撥㆒個給客戶,
//留19個於list#13。Pool剩餘2408-112*20=168.
select (0~16, -1 to end): 13
0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x0 0
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x25c2aa0 1
#10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19
#12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19
#14 (120) 0x0 0 #15 (128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
168
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 131
//說明:以㆘配置48,取pool劃分為3個區塊,撥㆒個
//給客戶,留2個於list#5。Pool剩餘168-48*3=24.
select (0~16, -1 to end): 5
0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x25c3c08 2
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x25c2aa0 1
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x25c2b60 19 #13(112) 0x25c3388 19
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置48,取pool劃分為3個區塊,撥㆒個
//給客戶,留2個於list#5。Pool剩餘168-48*3=24.
select (0~16, -1 to end): 5
0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x0 0 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x25c3c08 2
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x25c2aa0 1
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x25c2b60 19 #13(112) 0x25c3388 19
#14(120) 0x0 0 #15(128) 0x0 0
SGI memory pool 運行實證運行實證運行實證運行實證.10
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
24
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 132
SGI memory pool 運行實證運行實證運行實證運行實證.11
//說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額
//撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小
//(我將它設定為10000),因此記憶體不足,於是反求諸己取80區塊(#9)回填pool,再以之當做72區塊給客戶,餘8備用
select (0~16, -1 to end): 8
0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x25c3c08 2
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x25c2b60 19 #13(112) 0x25c3388 19
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額
//撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小
//(我將它設定為10000),因此記憶體不足,於是反求諸己取80區塊(#9)回填pool,再以之當做72區塊給客戶,餘8備用
select (0~16, -1 to end): 8
0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688
#0 (8) 0x25c2a08 19 #1 (16) 0x0 0
#2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x25c3c08 2
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c2480 16 #11(96) 0x25c1c00 19
#12(104) 0x25c2b60 19 #13(112) 0x25c3388 19
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
8
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 133
SGI memory pool 運行實證運行實證運行實證運行實證.12
//說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額撥給list#0,然後爭取
//72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000),
//因此記憶體不足,於是反求諸己取88區塊(#10)回填pool,再以之當做72區塊給客戶,餘16備用。
select (0~16, -1 to end): 8
0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688
#0 (8) 0x25c2ae8 20 #1 (16) 0x0 0
#2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x25c3c08 2
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19
#12(104) 0x25c2b60 19 #13(112) 0x25c3388 19
#14(120) 0x0 0 #15(128) 0x0 0
//說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額撥給list#0,然後爭取
//72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000),
//因此記憶體不足,於是反求諸己取88區塊(#10)回填pool,再以之當做72區塊給客戶,餘16備用。
select (0~16, -1 to end): 8
0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688
#0 (8) 0x25c2ae8 20 #1 (16) 0x0 0
#2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19
#4 (40) 0x0 0 #5 (48) 0x25c3c08 2
#6 (56) 0x0 0 #7 (64) 0x25c1958 9
#8 (72) 0x0 0 #9 (80) 0x0 0
#10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19
#12(104) 0x25c2b60 19 #13(112) 0x25c3388 19
#14(120) 0x0 0 #15(128) 0x0 0
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
16
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 134
SGI memory pool 運行實證運行實證運行實證運行實證.13
//說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應㆒個,
//於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4)
//的挹注,但此要求已超越system heap大小(我將它設定為10000,後述),
//因此記憶體不足,但反求諸己後仍得不到自由區塊,於是失敗。
//檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312,
//各個free lists內亦可能有些連續自由區塊。
select (0~16, -1 to end): 14
out-of-memory -- jjhou simulation.
//說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應㆒個,
//於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4)
//的挹注,但此要求已超越system heap大小(我將它設定為10000,後述),
//因此記憶體不足,但反求諸己後仍得不到自由區塊,於是失敗。
//檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312,
//各個free lists內亦可能有些連續自由區塊。
select (0~16, -1 to end): 14
out-of-memory -- jjhou simulation.
#0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15
16
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 144
small object allocator in Loki
Ref. MCD chap4, and Loki smallObj.cpp
4. SmallObject * 提供物件層次(object level)的服務
* 透通性 “ classes 繼承 SmallObject 即可享受服務
3. SmallObjAllocator * 能夠配置多種尺寸不同的小型物件,類似 std::alloc。
* 可根據參數而組態化(configurable)
* 內含 vector<FixedAllocator>
2. FixedAllocator * 配置某固定大小的物件,類似㆒個 free-list。
* 內含 vector<Chunk>,因此可以無限擴充(多個)
char[blockSize * blocks];
1. Chunk * 根據某特定大小來配置物件。有物件個數㆖限。
* 類似㆒個元素個數受限的 free-list。
* Init() 時 new char[blockSize * blocks]; 而後呼叫 Reset() 將
每隔 blockSize 個 bytes 的 1st byte 設為流水號碼(1~blocks),
以此做為 index 來管理 blocks.
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 145
Loki small object allocator, overview
f12 f16 f30 f32 f48 ...
負責 30 bytes 區塊
的配置和歸還
可由 client 自由添加
㆒定會按區塊大小排序
負責 48 bytes 區塊
的配置和歸還
blocksAvailable_
firstAvailableBlock_
pData_
1
16 bytes (blockSize_)
m
1
m
blocksAvailable_
firstAvailableBlock_
pData_
...
SmallObjAllocator 內含
vector<FixedAllocator> pool_;
FixedAllocator
內含 vector<Chunk>
Chunk
chunkSize_ (bytes)
numBlocks (blocks)
在 SGI STL ㆗,每㆒個 fixed size blocks 都被安置於 free list,並以其前 4 bytes 做為 singly
linked list 所需的 pointer。在 Loki ㆗,每㆒個 fixed size blocks 被安置於 chunk,那是塊連續空
間,不會變大也不會變小。與 free list 對應的是 vector<Chunk>。
#0 #1 #2 #3
32bytes
32bytes
32bytes
32bytes
32bytes
free_list[16]
0
第㆒塊傳
回給客戶
...2
3
Loki 把index當做ptr來使用
(當然可以),在chunk內實
施的(配置或歸還動作),
完全是標準 list 動作。
SGI STL
index
backfront
...
只有當某尺寸的需求至少
出現㆒次才產出㆒個對應
的 FixedAllocator
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 146
雖然要求區塊的時間次
序是:20, 64, 40,代表
創建 FixedAllocator 的時
間順序,但置於 vector
內的位置次序卻是 20,
40,64,以區塊大小排序。
侯捷認為排序意義不大.
Loki small object allocator, 1, allocate
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64
101
1
pData_
1
40 bytes
per block.
(40*102
=4080)
102
共 1 個
Chunks
SmallObjAllocator myAlloc(2048,256);
// 可應付小於 256 的各種大小的配置
void* p1 = (void*)myAlloc.Allocate(20);
void* p4 = (void*)myAlloc.Allocate(64);
void* p2 = (void*)myAlloc.Allocate(40);
void* p3 = (void*)myAlloc.Allocate(300); // 大於 256,將轉交系統處理
void* p5 = (void*)myAlloc.Allocate(64);
void* p6 = (void*)myAlloc.Allocate(64);
void* p7 = (void*)myAlloc.Allocate(64);
60
4
pData_
1
64 bytes
per block
64
共 1 個
Chunks
203
1
pData_
1
20 bytes
per block
204
共 1 個
Chunks
2
3
4
5
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_
indexindexindex
chunk 大小 =
20*204=
4080
chunk size(沒被用到,是設計遺漏吧。預設使用4096)
maxBlock
p1 p2 p4,p5,p6,p7
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
start
finish
start
finish
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 147
Loki small object allocator, 2, allocate
101
1
pData_
1
40 bytes
per block
102
共 1 個
Chunks
// 續㆖頁程式
void* ptr[100];
for (int i=0; i< 65; ++i)
ptr[i] = (void*)myAlloc.Allocate(64);
0
64
pData_
1
64 bytes
per block
64
共 2 個
Chunks
203
1
pData_
1
20 bytes
per block
204
共 1 個
Chunks
2
3
4
6
59
5
pData_
1
64
5
deallocChunk_
allocChunk_
index index
index
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
start
finish
start
finish
start
finish
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 148
Loki small object allocator, 3, deallocate
101
1
pData_
1
40 bytes
per block
102
共 1 個
Chunks
1
1
pData_
1
64
共 2 個
Chunks
203
1
pData_
1
20 bytes
per block
204
共 1 個
Chunks
2
3
4
6
59
5
pData_
5
64 bytes
per block
1
64
// 續㆖頁程式
myAlloc.Deallocate(p5,64);
// 註,其㆗運用deallocChunk_ 協助儘可能快速找到
// 「待刪區塊落在哪㆒個 chunk 內」
// 可觀察 SmallObj.cpp 的 VicinityFind()
p5
index index
index
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_ deallocChunk_
allocChunk_
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 149
Loki small object allocator, 4, deallocate
101
1
pData_
1
40 bytes
per block
102
共 1 個
Chunks
2
2
pData_
1
64
共 2 個
Chunks
203
1
pData_
1
20 bytes
per block
204
共 1 個
Chunks
2
3
4
6
59
5
pData_
5
64 bytes
per block
1
64
1
4
// 續㆖頁程式
myAlloc.Deallocate(p6,64);
p6
index index
index
deallocChunk_
allocChunk_
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 150
Loki small object allocator, 5, deallocate
101
1
pData_
1
40 bytes
per block
102
共 1 個
Chunks
3
0
pData_
1
64
共 2 個
Chunks
203
1
pData_
1
20 bytes
per block
204
共 1 個
Chunks
2
3
4
6
59
5
pData_
5
64 bytes
per block
2
64
1
4
// 續㆖頁程式
myAlloc.Deallocate(p4,64);
p4
index
index index
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_ deallocChunk_
allocChunk_
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 151
Loki small object allocator, 6, deallocate
101
1
pData_
1
40 bytes
per block
102
共 1 個
Chunks
4
3
pData_
1
64
共 2 個
Chunks
203
1
pData_
1
20 bytes
per block
204
共 1 個
Chunks
2
3
4
6
59
5
pData_
5
64 bytes
per block
2
64
1
0
// 續㆖頁程式
myAlloc.Deallocate(p7,64);
p7
index index
index
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_ deallocChunk_
allocChunk_
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 152
Loki small object allocator, 7, deallocate
102
0
pData_
1
40 bytes
per block
102
共 1 個
Chunks
64
4
pData_
5
64 bytes
per block
64
共 1 個
Chunks
204
0
pData_
1
20 bytes
per block
204
共 1 個
Chunks
0
1
2
3
// 續㆖頁程式
myAlloc.Deallocate(p1,20);
myAlloc.Deallocate(p2,40);
myAlloc.Deallocate(p3,300);
for (int i=0; i< 65; ++i)
myAlloc.Deallocate(ptr[i], 64);
真的釋放了㆒個
Chunk 還給系統
6
7
index index index
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_
allocChunk_
deallocChunk_
vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64
SmallObjAllocator 內含
vector<FixedAllocator>
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
start
finish
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 153
Loki small object allocator, level 1
Ref. MCD chap4, and Loki smallObj.cpp
class FixedAllocator
{
private:
struct Chunk
{
void Init(blockSize,blocks);
void* Allocate(_);
void Deallocate(_,_);
unsigned char* pData_;
unsigned char
firstAvailableBlock_,
blocksAvailable_;
};
...
};
class FixedAllocator
{
private:
struct Chunk
{
void Init(blockSize,blocks);
void* Allocate(_);
void Deallocate(_,_);
unsigned char* pData_;
unsigned char
firstAvailableBlock_,
blocksAvailable_;
};
...
};
blocksAvailable_ : 255
firstAvailableBlock_ : 0
pData_
1
2
254
255
… 253 區塊(blocks)...
Init(4,255);
4 bytes
blocksAvailable_ : 254
firstAvailableBlock_ : 1
pData_ 2
254
255
… 253 區塊(blocks)...
Allocate(4);
※ 以 array 管理,不若以 list 管理(如 SGI allocator)有彈性
※ 區塊大小不需配合 alignment
(但物件大小通常都由編譯器配合 alignment,那麼此㆒特性有何意義?)
這個 chunk 只負責供應
4byte block
現在,配置㆒個
4byte block
index
index
pData_ = new unsigned char
[blockSize * blocks];
這表示每個chunk內的區
塊數最多只能256
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 154
Loki small object allocator, level 1, 實作實作實作實作
Ref. MCD chap4, and Loki smallObj.cpp
void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks)
{
pData_ = new unsigned char[blockSize * blocks];
Reset(blockSize, blocks);
}
void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks)
{
pData_ = new unsigned char[blockSize * blocks];
Reset(blockSize, blocks);
}
void FixedAllocator::Chunk::Reset(std::size_t blockSize,
unsigned char blocks)
{
firstAvailableBlock_ = 0;
blocksAvailable_ = blocks;
unsigned char i = 0;
unsigned char* p = pData_;
for (; i != blocks; p += blockSize) //流水標示索引
*p = ++i;
}
void FixedAllocator::Chunk::Reset(std::size_t blockSize,
unsigned char blocks)
{
firstAvailableBlock_ = 0;
blocksAvailable_ = blocks;
unsigned char i = 0;
unsigned char* p = pData_;
for (; i != blocks; p += blockSize) //流水標示索引
*p = ++i;
}
void FixedAllocator::Chunk::Release()
{
delete[] pData_; //釋放自己
}
void FixedAllocator::Chunk::Release()
{
delete[] pData_; //釋放自己
}
void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
{
if (!blocksAvailable_) return 0; //此㆞無銀
unsigned char* pResult = //指向第㆒個可用區塊
pData_ + (firstAvailableBlock_ * blockSize);
firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區塊的索引
--blocksAvailable_;
return pResult;
}
void* FixedAllocator::Chunk::Allocate(std::size_t blockSize)
{
if (!blocksAvailable_) return 0; //此㆞無銀
unsigned char* pResult = //指向第㆒個可用區塊
pData_ + (firstAvailableBlock_ * blockSize);
firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區塊的索引
--blocksAvailable_;
return pResult;
}
void FixedAllocator::Chunk::Deallocate(void* p,
std::size_t blockSize)
{
unsigned char* toRelease = static_cast<unsigned char*>(p);
*toRelease = firstAvailableBlock_;
firstAvailableBlock_ = static_cast<unsigned char>(
(toRelease - pData_) / blockSize);
++blocksAvailable_; //可用區塊數加 1
}
void FixedAllocator::Chunk::Deallocate(void* p,
std::size_t blockSize)
{
unsigned char* toRelease = static_cast<unsigned char*>(p);
*toRelease = firstAvailableBlock_;
firstAvailableBlock_ = static_cast<unsigned char>(
(toRelease - pData_) / blockSize);
++blocksAvailable_; //可用區塊數加 1
}
1
2
3
4
...
...
254
1
pData_
255
2
配置第㆒個區塊
後的狀態:
253
2
pData_
255
配置第㆓個區塊
後的狀態:
254
0
pData_
2
255
歸還第㆒個區塊
後的狀態:
1
23
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 155
Ref. MCD chap4, and
Loki smallObj.cpp
Loki small object allocator, level 2
class FixedAllocator
{
private:
std::size_t blockSize_; // n
unsigned char numBlocks_; // m
typedef std::vector<Chunk> Chunks;
Chunks chunks_;
Chunk* allocChunk_; //初值是0
Chunk* deallocChunk_; //初值是0
//For ensuring proper copy semantics
mutable const FixedAllocator* prev_;
mutable const FixedAllocator* next_;
...
};
class FixedAllocator
{
private:
std::size_t blockSize_; // n
unsigned char numBlocks_; // m
typedef std::vector<Chunk> Chunks;
Chunks chunks_;
Chunk* allocChunk_; //初值是0
Chunk* deallocChunk_; //初值是0
//For ensuring proper copy semantics
mutable const FixedAllocator* prev_;
mutable const FixedAllocator* next_;
...
};
blocksAvailable_
firstAvailableBlock_
pData_
1
n bytes
blocksAvailable_
firstAvailableBlock_
pData_
m
1
m
blocksAvailable_
firstAvailableBlock_
pData_
1
m
chunks_front
back
FixedAllocator f16(16);
// 16 表示區塊大小
// 區塊數 m 為以㆘㆔者之㆒:
// m=C/n 或 256 或 8*n
// 其㆗ C 為 chunk 大小,預設4096
FixedAllocator f16(16);
// 16 表示區塊大小
// 區塊數 m 為以㆘㆔者之㆒:
// m=C/n 或 256 或 8*n
// 其㆗ C 為 chunk 大小,預設4096
㆒個 FixedAllocator object 功能就像 SGI
allocator 的㆒個 free list 似的。其內可有多個
'chunk',每個 'chunk' 管理 m 個 n bytes 區塊。
有趣的是,Loki 使用 DEFAULT_CHUNK_SIZE 而非
SmallObjAllocator::chunkSize_。後者完全沒被用到 (a bug)
以 cache 和 locality
強化 chunks_ 的搜尋速度
只用於 ctor, copy ctor, dtor,
Q:為什麼需要特別考慮 copy semantics 呢?
表現於 allocChunk_
和 deallocChunk_
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 156
Ref. MCD chap4, and
Loki smallObj.cpp
Loki small object allocator, level 2, 實作實作實作實作
#01 void* FixedAllocator::Allocate()
#02 {
#03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0)
#04 { //目前沒有標定chunk或該chunk已無可用區塊
#05 Chunks::iterator i = chunks_.begin(); //從頭找起
#06 for (;; ++i)
#07 {
#08 if (i == chunks_.end()) //到達尾端,都沒找著
#09 {
#10 // Initialize
#11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於末端
#12 Chunk& newChunk = chunks_.back(); //指向末端 chunk
#13 newChunk.Init(blockSize_, numBlocks_); //設好狀態
#14 allocChunk_ = &newChunk; //標定,稍後將對此 new chunk 取區塊
#15 deallocChunk_ = &chunks_.front(); //另㆒標定
#16 break;
#17 }
#18 if (i->blocksAvailable_ > 0)
#19 { //current chunk 有可用區塊
#20 allocChunk_ = &*i; //取其位址
#21 break; //不找了,退出迴圈
#22 }
#23 }
#24 }
#25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區塊
#26 }
#01 void* FixedAllocator::Allocate()
#02 {
#03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0)
#04 { //目前沒有標定chunk或該chunk已無可用區塊
#05 Chunks::iterator i = chunks_.begin(); //從頭找起
#06 for (;; ++i)
#07 {
#08 if (i == chunks_.end()) //到達尾端,都沒找著
#09 {
#10 // Initialize
#11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於末端
#12 Chunk& newChunk = chunks_.back(); //指向末端 chunk
#13 newChunk.Init(blockSize_, numBlocks_); //設好狀態
#14 allocChunk_ = &newChunk; //標定,稍後將對此 new chunk 取區塊
#15 deallocChunk_ = &chunks_.front(); //另㆒標定
#16 break;
#17 }
#18 if (i->blocksAvailable_ > 0)
#19 { //current chunk 有可用區塊
#20 allocChunk_ = &*i; //取其位址
#21 break; //不找了,退出迴圈
#22 }
#23 }
#24 }
#25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區塊
#26 }
//找遍每個chunk 直至找到擁有 free 區塊者.
每在某chunk找到可用區塊,㆘次就從該 chunk 找起
找到適當chunk就記錄㆘來,
㆘次就從那兒找起
假設 blockSize 是 20,blocks 個數是
4096 / 20 = 204, 不大於 256, OK.
假設 blockSize 是 16,blocks 個數是
4096 / 16 = 256, 不大於 256, OK
ref. VC's <limits.h>
其值為 0xFF
FixedAllocator::FixedAllocator(std::size_t blockSize)
: blockSize_(blockSize)
, deallocChunk_(0)
, allocChunk_(0)
{
prev_ = next_ = this; //以㆘計算並設定 numBlocks_;
std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize;
if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX;
else if (numBlocks == 0) numBlocks = 8 * blockSize;
numBlocks_ = static_cast<unsigned char>(numBlocks);
}
FixedAllocator::FixedAllocator(std::size_t blockSize)
: blockSize_(blockSize)
, deallocChunk_(0)
, allocChunk_(0)
{
prev_ = next_ = this; //以㆘計算並設定 numBlocks_;
std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize;
if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX;
else if (numBlocks == 0) numBlocks = 8 * blockSize;
numBlocks_ = static_cast<unsigned char>(numBlocks);
}
1
2
3
void FixedAllocator::Deallocate(void* p)
{
deallocChunk_ = VicinityFind(p);
DoDeallocate(p);
}
void FixedAllocator::Deallocate(void* p)
{
deallocChunk_ = VicinityFind(p);
DoDeallocate(p);
}
(內部函式, ㆘頁) 執行 deallocation. 假設
deallocChunk_ 已經指向正確的 chunk.
(內部函式, ㆘頁) 找出相對於某指標的
那個 chunk, 採用㆒種高效搜尋法.
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 157
Ref. MCD chap4, and
Loki smallObj.cpp
Loki small object allocator, level 2, 實作實作實作實作
#01 // FixedAllocator::VicinityFind (internal)
#02 // Finds the chunk corresponding to a pointer, using an efficient search
#03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p)
#04 {
#05 const std::size_t chunkLength = numBlocks_ * blockSize_;
#06
#07 Chunk* lo = deallocChunk_;
#08 Chunk* hi = deallocChunk_ + 1;
#09 Chunk* loBound = &chunks_.front();
#10 Chunk* hiBound = &chunks_.back() + 1;
#11
#12 for (;;) { //兵分兩路
#13 if (lo) { //㆖路未到盡頭
#14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength)
#15 return lo; //p指標值落於 lo 區間內
#16 if (lo == loBound) lo = 0; //到頂了,讓迴圈結束
#17 else --lo; //否則往 lo 的 front 繼續找
#18 }
#19
#20 if (hi) { //㆘路未到盡頭
#21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength)
#22 return hi; //p指標值落於 hi 區間內
#23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續找
#24 } //若到最尾則讓迴圈結束
#25 }
#26 return 0;
#27 }
#01 // FixedAllocator::VicinityFind (internal)
#02 // Finds the chunk corresponding to a pointer, using an efficient search
#03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p)
#04 {
#05 const std::size_t chunkLength = numBlocks_ * blockSize_;
#06
#07 Chunk* lo = deallocChunk_;
#08 Chunk* hi = deallocChunk_ + 1;
#09 Chunk* loBound = &chunks_.front();
#10 Chunk* hiBound = &chunks_.back() + 1;
#11
#12 for (;;) { //兵分兩路
#13 if (lo) { //㆖路未到盡頭
#14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength)
#15 return lo; //p指標值落於 lo 區間內
#16 if (lo == loBound) lo = 0; //到頂了,讓迴圈結束
#17 else --lo; //否則往 lo 的 front 繼續找
#18 }
#19
#20 if (hi) { //㆘路未到盡頭
#21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength)
#22 return hi; //p指標值落於 hi 區間內
#23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續找
#24 } //若到最尾則讓迴圈結束
#25 }
#26 return 0;
#27 }
blocksAvailable_
firstAvailableBlock_
pData_
1
m
lo = deallocChunk_lo = deallocChunk_
2
blocksAvailable_
firstAvailableBlock_
pData_
1
m
2
hi = deallocChunk_+1hi = deallocChunk_+1
如果找不到
就往 front 找去
如果找不到
就往 back 找去
front
back
如果這 p 並非當初從此系統㆗取得,這裡便肯定找不到對應的 chunk,
於是永遠不以 return 跳出 for loop,於是陷於無窮迴路之㆗。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 158
Ref. MCD chap4, and
Loki smallObj.cpp
Loki small object allocator, level 2, 實作實作實作實作
#0001 void FixedAllocator::DoDeallocate(void* p)
#0002 {
#0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory
#0004 deallocChunk_->Deallocate(p, blockSize_);
#0005
#0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) {
#0007 // deallocChunk_ 目前完全 free, 該 release 它嗎?
#0008
#0009 Chunk& lastChunk = chunks_.back(); //指向最後㆒個
#0010 //如果 "最後㆒個" 就是 "當前回收者"
#0011 if (&lastChunk == deallocChunk_) {
#0012 // 檢查是否擁有兩個 last chunks empty
#0013 // 這只需往 front 方向看前㆒個 chunk 便知.
#0014 if (chunks_.size() > 1 &&
#0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) {
#0016 // 是的有 2 free chunks, 拋棄最後㆒個
#0017 lastChunk.Release();
#0018 chunks_.pop_back();
#0019 allocChunk_ = deallocChunk_ = &chunks_.front();
#0020 }
#0021 return;
#0022 }
#0023
#0001 void FixedAllocator::DoDeallocate(void* p)
#0002 {
#0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory
#0004 deallocChunk_->Deallocate(p, blockSize_);
#0005
#0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) {
#0007 // deallocChunk_ 目前完全 free, 該 release 它嗎?
#0008
#0009 Chunk& lastChunk = chunks_.back(); //指向最後㆒個
#0010 //如果 "最後㆒個" 就是 "當前回收者"
#0011 if (&lastChunk == deallocChunk_) {
#0012 // 檢查是否擁有兩個 last chunks empty
#0013 // 這只需往 front 方向看前㆒個 chunk 便知.
#0014 if (chunks_.size() > 1 &&
#0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) {
#0016 // 是的有 2 free chunks, 拋棄最後㆒個
#0017 lastChunk.Release();
#0018 chunks_.pop_back();
#0019 allocChunk_ = deallocChunk_ = &chunks_.front();
#0020 }
#0021 return;
#0022 }
#0023
blocksAvailable_
firstAvailableBlock_
pData_
1
m
deallocChunk_deallocChunk_
2
待刪之 p 已確定
落在這個 chunk
#0024 if (lastChunk.blocksAvailable_ == numBlocks_) {
#0025 //2 free chunks, 拋棄最後㆒個
#0026 lastChunk.Release();
#0027 chunks_.pop_back();
#0028 allocChunk_ = deallocChunk_;
#0029 }
#0030 else {
#0031 //將free chunk 與後㆒個chunk 對調
#0032 std::swap(*deallocChunk_, lastChunk);
#0033 allocChunk_ = &chunks_.back();
#0034 }
#0035 }
#0036 }
#0024 if (lastChunk.blocksAvailable_ == numBlocks_) {
#0025 //2 free chunks, 拋棄最後㆒個
#0026 lastChunk.Release();
#0027 chunks_.pop_back();
#0028 allocChunk_ = deallocChunk_;
#0029 }
#0030 else {
#0031 //將free chunk 與後㆒個chunk 對調
#0032 std::swap(*deallocChunk_, lastChunk);
#0033 allocChunk_ = &chunks_.back();
#0034 }
#0035 }
#0036 }
這裡殺掉deallocChunk_ 似乎較好,
這就可以保持最後㆒個 chunk 為 free
以目前實作來看,似乎有可能某種情況
㆘不歸還chunk 給系統。試證之。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 159
Ref. MCD chap4, and
Loki smallObj.cpp
Loki small object allocator, level 3
class SmallObjAllocator
{
private:
typedef std::vector<FixedAllocator> Pool;
Pool pool_;
FixedAllocator* pLastAlloc_;
FixedAllocator* pLastDealloc_;
std::size_t chunkSize_;
std::size_t maxObjectSize_;
public:
void* Allocate(std::size_t nBytes);
void Deallocate(void* p, std::size_t size);
};
class SmallObjAllocator
{
private:
typedef std::vector<FixedAllocator> Pool;
Pool pool_;
FixedAllocator* pLastAlloc_;
FixedAllocator* pLastDealloc_;
std::size_t chunkSize_;
std::size_t maxObjectSize_;
public:
void* Allocate(std::size_t nBytes);
void Deallocate(void* p, std::size_t size);
};
f48
f32
f30
f16
f12
pool_
這有點像 SGI allocator 的
free_list[16]
但此處個數不限 16,區塊
大小亦不限為 8 倍數
...
負責 12 bytes 區塊
的配置和歸還
負責 32 bytes 區塊
的配置和歸還
負責 48 bytes 區塊
的配置和歸還
SmallObjAllocator myAlloc(2048,256);
// 第㆒參數表 ChunkSize
// 第㆓參數表 maxObjSize
// 這樣的彈性表示
// 它可產出「區塊大小低於 maxObjSize」
// 的任何 FixedAllocator 出來。
SmallObjAllocator myAlloc(2048,256);
// 第㆒參數表 ChunkSize
// 第㆓參數表 maxObjSize
// 這樣的彈性表示
// 它可產出「區塊大小低於 maxObjSize」
// 的任何 FixedAllocator 出來。
以 cache 和 binary search
強化 pool_ 的搜尋速度
每個元素都是
FixedAllocator
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 160
Ref. MCD chap4, and
Loki smallObj.cpp
Loki small object allocator, level 3, 實作實作實作實作
SmallObjAllocator::SmallObjAllocator(
std::size_t chunkSize,
std::size_t maxObjectSize)
: pLastAlloc_(0), pLastDealloc_(0)
, chunkSize_(chunkSize), maxObjectSize_(maxObjectSize)
{
}
SmallObjAllocator::SmallObjAllocator(
std::size_t chunkSize,
std::size_t maxObjectSize)
: pLastAlloc_(0), pLastDealloc_(0)
, chunkSize_(chunkSize), maxObjectSize_(maxObjectSize)
{
}
#01 void* SmallObjAllocator::Allocate(std::size_t numBytes)
#02 {
#03 if (numBytes > maxObjectSize_) //區塊大小超過服務範圍
#04 return operator new(numBytes); //由 global operator new 服務
#05
#06 if (pLastAlloc_ && //㆖次那 FixedAllocator 合適否
#07 pLastAlloc_->BlockSize() == numBytes) {
#08 return pLastAlloc_->Allocate(); //從㆗取區塊
#09 } //至此表示㆖次那個 FixedAllocator 不適當
#10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出適當位置
#11 if (i == pool_.end() || i->BlockSize() != numBytes) {
#12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚無 chunk) 並插入vector
#13 pLastDealloc_ = &*pool_.begin();
#14 }
#15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位址
#16 return pLastAlloc_->Allocate(); //從㆗取區塊
#17 }
#01 void* SmallObjAllocator::Allocate(std::size_t numBytes)
#02 {
#03 if (numBytes > maxObjectSize_) //區塊大小超過服務範圍
#04 return operator new(numBytes); //由 global operator new 服務
#05
#06 if (pLastAlloc_ && //㆖次那 FixedAllocator 合適否
#07 pLastAlloc_->BlockSize() == numBytes) {
#08 return pLastAlloc_->Allocate(); //從㆗取區塊
#09 } //至此表示㆖次那個 FixedAllocator 不適當
#10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出適當位置
#11 if (i == pool_.end() || i->BlockSize() != numBytes) {
#12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚無 chunk) 並插入vector
#13 pLastDealloc_ = &*pool_.begin();
#14 }
#15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位址
#16 return pLastAlloc_->Allocate(); //從㆗取區塊
#17 }
#01 // Deallocates memory previously allocated with Allocate
#02 // (undefined behavior if you pass any other pointer)
#03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt
#04 { //若超過服務範圍,轉給 global operator delete() 去做
#05 if (numBytes > maxObjectSize_) return operator delete(p);
#06 //標定者為適當之 FixedAllocator,令它 Deallocate(p).
#07 if (pLastDealloc_ && pLastDealloc_->BlockSize() ==
#08 numBytes) {
#09 pLastDealloc_->Deallocate(p);
#10 return;
#11 } //至此,以㆘搜尋最適當之 FixedAllocator(㆒定有)
#12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(
#13 numBytes);
#14 pLastDealloc_ = &*i; //取其位址
#15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p)
#16 }
#01 // Deallocates memory previously allocated with Allocate
#02 // (undefined behavior if you pass any other pointer)
#03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt
#04 { //若超過服務範圍,轉給 global operator delete() 去做
#05 if (numBytes > maxObjectSize_) return operator delete(p);
#06 //標定者為適當之 FixedAllocator,令它 Deallocate(p).
#07 if (pLastDealloc_ && pLastDealloc_->BlockSize() ==
#08 numBytes) {
#09 pLastDealloc_->Deallocate(p);
#10 return;
#11 } //至此,以㆘搜尋最適當之 FixedAllocator(㆒定有)
#12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end()
#13 numBytes);
#14 pLastDealloc_ = &*i; //取其位址
#15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p)
#16 }
Q: lower_bound() 操作的對象應該是sorted,也就需要 op<,
但 FixedAllocator 沒有 op< 呀!
A: 有的,是個in-class inline:
bool operator<(size_t rhs) const { return blockSize_ < rhs; }
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 161
MFC's CPlex
CPlex object
this
nMax * cbElement
void* CPlex::data() { return this+1; }
...
CPlex 扮演 memory pool 角色
pNext
pNext
CPlex* PASCAL CPlex::Create(CPlex*& pHead,
UINT nMax, UINT cbElement)
{ //配置記憶體後順便鏈接起來.
CPlex* p = (CPlex*) new
BYTE[sizeof(CPlex) + nMax * cbElement];
p->pNext = pHead;
pHead = p; // change head (adds in reverse order)
return p;
}
CPlex* PASCAL CPlex::Create(CPlex*& pHead,
UINT nMax, UINT cbElement)
{ //配置記憶體後順便鏈接起來.
CPlex* p = (CPlex*) new
BYTE[sizeof(CPlex) + nMax * cbElement];
p->pNext = pHead;
pHead = p; // change head (adds in reverse order)
return p;
}
void CPlex::FreeDataChain()
{
CPlex* p = this;
while (p != NULL) {
BYTE* bytes = (BYTE*) p;
CPlex* pNext = p->pNext;
delete[] bytes;
p = pNext;
} //歸還每㆒長條
}
void CPlex::FreeDataChain()
{
CPlex* p = this;
while (p != NULL) {
BYTE* bytes = (BYTE*) p;
CPlex* pNext = p->pNext;
delete[] bytes;
p = pNext;
} //歸還每㆒長條
}
struct CPlex
{
CPlex* pNext;
#if (_AFX_PACKING >= 8)
DWORD dwReserved[1]; // align on 8 byte boundary
#endif
void* data() { return this+1; }
static CPlex* PASCAL Create(CPlex*& head,
UINT nMax, UINT cbElement);
void FreeDataChain(); // free this one and links
};
struct CPlex
{
CPlex* pNext;
#if (_AFX_PACKING >= 8)
DWORD dwReserved[1]; // align on 8 byte boundary
#endif
void* data() { return this+1; }
static CPlex* PASCAL Create(CPlex*& head,
UINT nMax, UINT cbElement);
void FreeDataChain(); // free this one and links
};
全
部
清
除
每次配置㆒長條
Ref. MFC's afxplex_.h & plex.cpp
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 162
MFC's CPtrList
CPlex object
pos pospos
CPtrList object :
GetHeadPosition()
GetNext()
m_pNodeHead m_pNodeTail
m_pNodeFree
m_pBlocks
m_nBlockSize (default 10)
CNode object in free list
void* CPlex::data() { return this+1; }
used CNode object (in "Ptr-List")
interface
implementation
+ m_nBlockSize - 1
...
...
CPlex扮演
memory pool
角色
個數:m_nCount
Ref. MFC's afxcoll.h & list_p.cpp
CPtrList 的每個節點都來自
pooled allocator CPlex,因此
都不帶 cookie。
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 166
Boost's Pool 主要有主要有主要有主要有 6 files, poolfwd.hpp宣告各個宣告各個宣告各個宣告各個 classes
#include <boost/config.hpp> // for workarounds
#include <cstddef> // std::size_t
#include <boost/pool/detail/mutex.hpp>
template <typename SizeType = std::size_t>
class simple_segregated_storage;
struct default_user_allocator_new_delete;
struct default_user_allocator_malloc_free;
template <typename UserAllocator = default_user_allocator_new_delete>
class pool;
template <typename T, typename UserAllocator = default_user_allocator_new_delete>
class object_pool;
template <typename Tag, unsigned RequestedSize,
typename UserAllocator = default_user_allocator_new_delete,
typename Mutex = details::pool::default_mutex,
unsigned NextSize = 32>
struct singleton_pool;
template <typename T,
typename UserAllocator = default_user_allocator_new_delete,
typename Mutex = details::pool::default_mutex,
unsigned NextSize = 32>
class pool_allocator;
#include <boost/config.hpp> // for workarounds
#include <cstddef> // std::size_t
#include <boost/pool/detail/mutex.hpp>
template <typename SizeType = std::size_t>
class simple_segregated_storage;
struct default_user_allocator_new_delete;
struct default_user_allocator_malloc_free;
template <typename UserAllocator = default_user_allocator_new_delete>
class pool;
template <typename T, typename UserAllocator = default_user_allocator_new_delete>
class object_pool;
template <typename Tag, unsigned RequestedSize,
typename UserAllocator = default_user_allocator_new_delete,
typename Mutex = details::pool::default_mutex,
unsigned NextSize = 32>
struct singleton_pool;
template <typename T,
typename UserAllocator = default_user_allocator_new_delete,
typename Mutex = details::pool::default_mutex,
unsigned NextSize = 32>
class pool_allocator;
預設使用這個,㆘頁
各個 classes
定義於...
poolfwd.hpp
另有個 boost::details::PODptr 也定義於 pool.hpp
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 168
Boost Pool 的㆕個主要的㆕個主要的㆕個主要的㆕個主要 classes 的的的的關係關係關係關係
pool
simple_segregated_storage
template <typename UserAllocator=…>
class pool: protected simple_segregated_storage<
typename UserAllocator::size_type>
template <typename SizeType>
class simple_segregated_storage
template <typename T, typename UserAllocator=…>
class object_pool: protected pool<UserAllocator>object_pool
template <typename T,
typename UserAllocator,
typename Mutex,
unsigned NextSize>
class pool_allocator
default_user_allocator_malloc_freedefault_user_allocator_malloc_free
default_user_allocator_new_deletedefault_user_allocator_new_delete
T
T
T
template <typename Tag,
unsigned RequestedSize,
typename UserAllocator,
typename Mutex,
unsigned NextSize>
struct singleton_pool
#store()
#nextof(void* const):void*&
malloc():T*
free(T*)
construct():T*
destroy(T*)
#first : void*指向 free-list 的
第㆒個chunk.
#nextof(void* const)
segregate(void*,size_t, size_t, void*=0)
add_block(void*,size_t, size_t)
malloc()
free(void*)
list:PODptr
requested_size //區塊大小
next_size=32 //區塊數
全都沒有全都沒有全都沒有全都沒有
virtual
functions
PODptr
ptr:char*
sz:size_type
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 173
pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示
boost::pool::ordered_malloc_need_resize()的動作:
首先計算 partition_size為 8,再計算 POD_size為264,然後配置 264 bytes 大區塊,
然後設立㆒個局部變數 node(是個 PODptr):
ptr 264
264 = 32*8 + 4 + 4
(這㆒整個大區塊就是所謂POD)
0 0 0 32 8
fooPool
然後切割、調整㆘次配置之小區塊數(兩倍增長)、整合:
264 64 8
fooPool
...
264 bytes
32個小區塊
0 0
0 0
node
class Foo {
public:
Foo(char c, long v)
: type(c),value(v) { }
private:
char type;
long value;
};
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 174
pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示
最後,給出㆒個(也就是 ordered_malloc_need_resize() 內部呼叫 store().malloc() 造成):
264 64 8
fooPool
...
264 bytes
32個小區塊
0 0
㆘次用戶再呼叫 fooPool.construct() 或 foolPool.malloc() 時,直接給出:
264 64 8
fooPool
...
264 bytes
32個小區塊
0 0
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 175
pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示
配置 32 次之後,變成(注意 first 指標又變成 null):
0 264 64 8
fooPool
...
264 bytes
32個小區塊
0 0
再次配置時,所有小區塊都已用完,於是串接另㆒個大區塊(依然切割、調整㆘次
配置之小區塊數、整合):
520 128 8
fooPool
...
264 bytes
32個小區塊
0 0
...
520 bytes = 64*8 + 4 + 4
64個小區塊
264
0x007E0490
0x007E0250
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 176
pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示
配置㆒個後: 520 128 8fooPool
...
264 bytes
32個小區塊
0 0
...
520 bytes = 64*8 + 4 + 4
64個小區塊
264
配置多個後: 0 520 128 8fooPool
...
264 bytes
32個小區塊
0 0
...
520 bytes = 64*8 + 4 + 4
64個小區塊
264
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 177
pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示
...
264 bytes
32個小區塊
0 0
...
520 bytes = 64*8 + 4 + 4
64個小區塊
264
再次配置時,所有小區塊都已用完,於是串接另㆒個大區塊(依然切割、調整㆘次
配置之小區塊數、整合):
...
1032 bytes = 128*8 + 4 + 4
128個小區塊
520
007E0250
007E0490
007D09A4
1032 256 8
fooPool 表示這個 pool 為 8-bytes 服務
㆘次配置的 chunks 個數。
這裡的記錄將會是 64,128,256,...
目前的 block 大小。
這裡的記錄將會是 264,520,1032,...
指向目前可用的 chunk.
指向目前使用㆗的 block
1
2
3
4
5
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 179
㆔層㆔層㆔層㆔層. Memory Deallocation
void ordered_free(void * const chunk)
{
// This (slower) implementation of 'free' places
// the memoryback in the list in its proper order.
// Find where "chunk" goes in the free list
void * const loc = find_prev(chunk);
// Place either at beginning or in middle/end
if (loc == 0)
free(chunk);
else {
nextof(chunk) = nextof(loc);
nextof(loc) = chunk;
}
}
void ordered_free(void * const chunk)
{
// This (slower) implementation of 'free' places
// the memoryback in the list in its proper order.
// Find where "chunk" goes in the free list
void * const loc = find_prev(chunk);
// Place either at beginning or in middle/end
if (loc == 0)
free(chunk);
else {
nextof(chunk) = nextof(loc);
nextof(loc) = chunk;
}
}
find_prev(void * const ptr)
{ // Handle border case
if (first == 0 || std::greater<void *>()(first, ptr))
return 0;
void * iter = first;
while (true) {
// if we're about to hit the end or
// if we've found where "ptr" goes
if (nextof(iter) == 0 ||
std::greater<void *>()(nextof(iter), ptr))
return iter;
iter = nextof(iter);
}
}
find_prev(void * const ptr)
{ // Handle border case
if (first == 0 || std::greater<void *>()(first, ptr))
return 0;
void * iter = first;
while (true) {
// if we're about to hit the end or
// if we've found where "ptr" goes
if (nextof(iter) == 0 ||
std::greater<void *>()(nextof(iter), ptr))
return iter;
iter = nextof(iter);
}
}
void free(void * const chunk)
{
nextof(chunk) = first;
first = chunk;
}
void free(void * const chunk)
{
nextof(chunk) = first;
first = chunk;
}
如果目前有㆒個 block,已分配 5 chunks 出去,
first 指向 6th chunk:
264 64 8
... 0 0
那麼當歸還 2rd chunk 指標時:
264 64 8
... 0 0
歸還 4th chunk 指標時:
264 64 8
... 0 0
p2 p4
p4
iter 最後停在這裡,
傳回被 loc 承接
滿足這個條件
first
first
first
這個迴圈將找出first之後
的㆒個 "適當" 位置
滿足這個條件
這個algorithm㆒定和
"blocks 的串接永遠從低
位址至高位址排列" 有關
jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 180
㆔層㆔層㆔層㆔層. Memory Deallocation
void ordered_free(void * const chunk)
{
// This (slower) implementation of 'free' places
// the memoryback in the list in its proper order.
// Find where "chunk" goes in the free list
void * const loc = find_prev(chunk);
// Place either at beginning or in middle/end
if (loc == 0)
free(chunk);
else {
nextof(chunk) = nextof(loc);
nextof(loc) = chunk;
}
}
void ordered_free(void * const chunk)
{
// This (slower) implementation of 'free' places
// the memoryback in the list in its proper order.
// Find where "chunk" goes in the free list
void * const loc = find_prev(chunk);
// Place either at beginning or in middle/end
if (loc == 0)
free(chunk);
else {
nextof(chunk) = nextof(loc);
nextof(loc) = chunk;
}
}
find_prev(void * const ptr)
{ // Handle border case
if (first == 0 || std::greater<void *>()(first, ptr))
return 0;
void * iter = first;
while (true) {
// if we're about to hit the end or
// if we've found where "ptr" goes
if (nextof(iter) == 0 ||
std::greater<void *>()(nextof(iter), ptr))
return iter;
iter = nextof(iter);
}
}
find_prev(void * const ptr)
{ // Handle border case
if (first == 0 || std::greater<void *>()(first, ptr))
return 0;
void * iter = first;
while (true) {
// if we're about to hit the end or
// if we've found where "ptr" goes
if (nextof(iter) == 0 ||
std::greater<void *>()(nextof(iter), ptr))
return iter;
iter = nextof(iter);
}
}
void free(void * const chunk)
{
nextof(chunk) = first;
first = chunk;
}
void free(void * const chunk)
{
nextof(chunk) = first;
first = chunk;
}
如果目前有 3 個 blocks 如㆘:
1032 256 8
...
32個小區塊
0 0
...
64個小區塊
264
...
128個小區塊
520
007E0250
007E0490
007D09A4
0
p4
歸還 p4 會變成這樣
(見㆘頁)
iter 最後停在這裡,
傳回被 loc 承接
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice
Design Patterns and Adapters in Practice

Más contenido relacionado

La actualidad más candente

Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13Abu Saleh
 
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...ssuserd6b1fd
 
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...ssuserd6b1fd
 
Paradigmas de Linguagens de Programacao - Aula #5
Paradigmas de Linguagens de Programacao - Aula #5Paradigmas de Linguagens de Programacao - Aula #5
Paradigmas de Linguagens de Programacao - Aula #5Ismar Silveira
 
46630497 fun-pointer-1
46630497 fun-pointer-146630497 fun-pointer-1
46630497 fun-pointer-1AmIt Prasad
 
C tech questions
C tech questionsC tech questions
C tech questionsvijay00791
 
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...ssuserd6b1fd
 
C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0Yaser Zhian
 
Data structure week 3
Data structure week 3Data structure week 3
Data structure week 3karmuhtam
 
Data structure week 1
Data structure week 1Data structure week 1
Data structure week 1karmuhtam
 
FP 201 - Unit 6
FP 201 - Unit 6FP 201 - Unit 6
FP 201 - Unit 6rohassanie
 

La actualidad más candente (20)

Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13
 
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 5 of 5 by...
 
Ds lab handouts
Ds lab handoutsDs lab handouts
Ds lab handouts
 
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
Notes for C Programming for MCA, BCA, B. Tech CSE, ECE and MSC (CS) 4 of 5 by...
 
Paradigmas de Linguagens de Programacao - Aula #5
Paradigmas de Linguagens de Programacao - Aula #5Paradigmas de Linguagens de Programacao - Aula #5
Paradigmas de Linguagens de Programacao - Aula #5
 
Unit 3
Unit 3 Unit 3
Unit 3
 
C programming slide c04
C programming slide c04C programming slide c04
C programming slide c04
 
report
reportreport
report
 
Fp201 unit5 1
Fp201 unit5 1Fp201 unit5 1
Fp201 unit5 1
 
c programming
c programmingc programming
c programming
 
46630497 fun-pointer-1
46630497 fun-pointer-146630497 fun-pointer-1
46630497 fun-pointer-1
 
C tech questions
C tech questionsC tech questions
C tech questions
 
Fp201 unit4
Fp201 unit4Fp201 unit4
Fp201 unit4
 
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
Notes for C++ Programming / Object Oriented C++ Programming for MCA, BCA and ...
 
C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0
 
7 functions
7  functions7  functions
7 functions
 
Data structure week 3
Data structure week 3Data structure week 3
Data structure week 3
 
String searching
String searchingString searching
String searching
 
Data structure week 1
Data structure week 1Data structure week 1
Data structure week 1
 
FP 201 - Unit 6
FP 201 - Unit 6FP 201 - Unit 6
FP 201 - Unit 6
 

Destacado

Neo Yachting - Luxury Yacht Charter - catalog January 2012
Neo Yachting - Luxury Yacht Charter - catalog January 2012Neo Yachting - Luxury Yacht Charter - catalog January 2012
Neo Yachting - Luxury Yacht Charter - catalog January 2012Yachting.vg
 
Social media roi tracking london 11-22-2010 - final
Social media roi tracking london  11-22-2010 - finalSocial media roi tracking london  11-22-2010 - final
Social media roi tracking london 11-22-2010 - finalMarshall Sponder
 
Mevlid kandili idris yavuzyiğit
Mevlid kandili idris yavuzyiğitMevlid kandili idris yavuzyiğit
Mevlid kandili idris yavuzyiğitSalım Selvi
 
Pembelajaran di era social network
Pembelajaran di era social networkPembelajaran di era social network
Pembelajaran di era social networkAsep Sufyan Tsauri
 
The yellow wallpaper charlotte perkins gilman
The yellow wallpaper   charlotte perkins gilmanThe yellow wallpaper   charlotte perkins gilman
The yellow wallpaper charlotte perkins gilmanstaefenia sun
 
Eve.St-Jean Resumé
Eve.St-Jean ResuméEve.St-Jean Resumé
Eve.St-Jean ResuméEve St-Jean
 
[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam
[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam
[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh namtrongphuckhtn
 
Bab 1 perm. sepakbola
Bab 1 perm. sepakbolaBab 1 perm. sepakbola
Bab 1 perm. sepakbolaBudi Hermono
 
Подбор респондентов для любых исследований
Подбор респондентов для любых исследованийПодбор респондентов для любых исследований
Подбор респондентов для любых исследованийKaterina Umnova
 

Destacado (20)

Herpes
HerpesHerpes
Herpes
 
Neo Yachting - Luxury Yacht Charter - catalog January 2012
Neo Yachting - Luxury Yacht Charter - catalog January 2012Neo Yachting - Luxury Yacht Charter - catalog January 2012
Neo Yachting - Luxury Yacht Charter - catalog January 2012
 
Social media roi tracking london 11-22-2010 - final
Social media roi tracking london  11-22-2010 - finalSocial media roi tracking london  11-22-2010 - final
Social media roi tracking london 11-22-2010 - final
 
Mevlid kandili idris yavuzyiğit
Mevlid kandili idris yavuzyiğitMevlid kandili idris yavuzyiğit
Mevlid kandili idris yavuzyiğit
 
Politika Budur
Politika BudurPolitika Budur
Politika Budur
 
Digital Signage
Digital SignageDigital Signage
Digital Signage
 
Pembelajaran di era social network
Pembelajaran di era social networkPembelajaran di era social network
Pembelajaran di era social network
 
The yellow wallpaper charlotte perkins gilman
The yellow wallpaper   charlotte perkins gilmanThe yellow wallpaper   charlotte perkins gilman
The yellow wallpaper charlotte perkins gilman
 
China
ChinaChina
China
 
曹雯雯
曹雯雯曹雯雯
曹雯雯
 
Recepción de facturas
Recepción de facturasRecepción de facturas
Recepción de facturas
 
2. angina
2. angina2. angina
2. angina
 
Sindrom nefrolik
Sindrom nefrolikSindrom nefrolik
Sindrom nefrolik
 
Eve.St-Jean Resumé
Eve.St-Jean ResuméEve.St-Jean Resumé
Eve.St-Jean Resumé
 
[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam
[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam
[Vnmath.com] chuyen-de-toan-ltdh-thukhoa-dang thanh nam
 
Bab 1 perm. sepakbola
Bab 1 perm. sepakbolaBab 1 perm. sepakbola
Bab 1 perm. sepakbola
 
History of Logic
History of LogicHistory of Logic
History of Logic
 
MOHD ZAMA KHAN CV
MOHD ZAMA KHAN CVMOHD ZAMA KHAN CV
MOHD ZAMA KHAN CV
 
Informed consent
Informed consentInformed consent
Informed consent
 
Подбор респондентов для любых исследований
Подбор респондентов для любых исследованийПодбор респондентов для любых исследований
Подбор респондентов для любых исследований
 

Similar a Design Patterns and Adapters in Practice

COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxCOMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxmonicafrancis71118
 
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxCOMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxcargillfilberto
 
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxCOMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxdrandy1
 
Templates presentation
Templates presentationTemplates presentation
Templates presentationmalaybpramanik
 
Programming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdf
Programming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdfProgramming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdf
Programming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdfssuser6254411
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonCodemotion
 
TEMPLATES IN JAVA
TEMPLATES IN JAVATEMPLATES IN JAVA
TEMPLATES IN JAVAMuskanSony
 
Meetup C++ A brief overview of c++17
Meetup C++  A brief overview of c++17Meetup C++  A brief overview of c++17
Meetup C++ A brief overview of c++17Daniel Eriksson
 
Introducing PHP Latest Updates
Introducing PHP Latest UpdatesIntroducing PHP Latest Updates
Introducing PHP Latest UpdatesIftekhar Eather
 
Functions And Header Files In C++ | Bjarne stroustrup
Functions And Header Files In C++ | Bjarne stroustrupFunctions And Header Files In C++ | Bjarne stroustrup
Functions And Header Files In C++ | Bjarne stroustrupSyedHaroonShah4
 
2.overview of c++ ________lecture2
2.overview of c++  ________lecture22.overview of c++  ________lecture2
2.overview of c++ ________lecture2Warui Maina
 
JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020Joseph Kuo
 
New microsoft office word document (2)
New microsoft office word document (2)New microsoft office word document (2)
New microsoft office word document (2)rashmita_mishra
 

Similar a Design Patterns and Adapters in Practice (20)

COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxCOMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
 
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxCOMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
 
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docxCOMM 166 Final Research Proposal GuidelinesThe proposal should.docx
COMM 166 Final Research Proposal GuidelinesThe proposal should.docx
 
Templates presentation
Templates presentationTemplates presentation
Templates presentation
 
Templates2
Templates2Templates2
Templates2
 
Ppt of c vs c#
Ppt of c vs c#Ppt of c vs c#
Ppt of c vs c#
 
Summary of C++17 features
Summary of C++17 featuresSummary of C++17 features
Summary of C++17 features
 
Programming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdf
Programming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdfProgramming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdf
Programming For Big Data [ Submission DvcScheduleV2.cpp and StaticA.pdf
 
TWINS: OOP and FP - Warburton
TWINS: OOP and FP - WarburtonTWINS: OOP and FP - Warburton
TWINS: OOP and FP - Warburton
 
TEMPLATES IN JAVA
TEMPLATES IN JAVATEMPLATES IN JAVA
TEMPLATES IN JAVA
 
Meetup C++ A brief overview of c++17
Meetup C++  A brief overview of c++17Meetup C++  A brief overview of c++17
Meetup C++ A brief overview of c++17
 
Introducing PHP Latest Updates
Introducing PHP Latest UpdatesIntroducing PHP Latest Updates
Introducing PHP Latest Updates
 
14 operator overloading
14 operator overloading14 operator overloading
14 operator overloading
 
Functions And Header Files In C++ | Bjarne stroustrup
Functions And Header Files In C++ | Bjarne stroustrupFunctions And Header Files In C++ | Bjarne stroustrup
Functions And Header Files In C++ | Bjarne stroustrup
 
2.overview of c++ ________lecture2
2.overview of c++  ________lecture22.overview of c++  ________lecture2
2.overview of c++ ________lecture2
 
functions
functionsfunctions
functions
 
JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020JCConf 2020 - New Java Features Released in 2020
JCConf 2020 - New Java Features Released in 2020
 
Design patterns
Design patternsDesign patterns
Design patterns
 
New microsoft office word document (2)
New microsoft office word document (2)New microsoft office word document (2)
New microsoft office word document (2)
 
08 -functions
08  -functions08  -functions
08 -functions
 

Más de yiditushe

Spring入门纲要
Spring入门纲要Spring入门纲要
Spring入门纲要yiditushe
 
J Bpm4 1中文用户手册
J Bpm4 1中文用户手册J Bpm4 1中文用户手册
J Bpm4 1中文用户手册yiditushe
 
性能测试实践2
性能测试实践2性能测试实践2
性能测试实践2yiditushe
 
性能测试实践1
性能测试实践1性能测试实践1
性能测试实践1yiditushe
 
性能测试技术
性能测试技术性能测试技术
性能测试技术yiditushe
 
Load runner测试技术
Load runner测试技术Load runner测试技术
Load runner测试技术yiditushe
 
J2 ee性能测试
J2 ee性能测试J2 ee性能测试
J2 ee性能测试yiditushe
 
面向对象的Js培训
面向对象的Js培训面向对象的Js培训
面向对象的Js培训yiditushe
 
Flex3中文教程
Flex3中文教程Flex3中文教程
Flex3中文教程yiditushe
 
开放源代码的全文检索Lucene
开放源代码的全文检索Lucene开放源代码的全文检索Lucene
开放源代码的全文检索Luceneyiditushe
 
基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍yiditushe
 
Lucene In Action
Lucene In ActionLucene In Action
Lucene In Actionyiditushe
 
Lucene2 4学习笔记1
Lucene2 4学习笔记1Lucene2 4学习笔记1
Lucene2 4学习笔记1yiditushe
 
Lucene2 4 Demo
Lucene2 4 DemoLucene2 4 Demo
Lucene2 4 Demoyiditushe
 
Lucene 全文检索实践
Lucene 全文检索实践Lucene 全文检索实践
Lucene 全文检索实践yiditushe
 
Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析yiditushe
 
7 面向对象设计原则
7 面向对象设计原则7 面向对象设计原则
7 面向对象设计原则yiditushe
 
10 团队开发
10  团队开发10  团队开发
10 团队开发yiditushe
 
9 对象持久化与数据建模
9  对象持久化与数据建模9  对象持久化与数据建模
9 对象持久化与数据建模yiditushe
 
8 Uml构架建模
8  Uml构架建模8  Uml构架建模
8 Uml构架建模yiditushe
 

Más de yiditushe (20)

Spring入门纲要
Spring入门纲要Spring入门纲要
Spring入门纲要
 
J Bpm4 1中文用户手册
J Bpm4 1中文用户手册J Bpm4 1中文用户手册
J Bpm4 1中文用户手册
 
性能测试实践2
性能测试实践2性能测试实践2
性能测试实践2
 
性能测试实践1
性能测试实践1性能测试实践1
性能测试实践1
 
性能测试技术
性能测试技术性能测试技术
性能测试技术
 
Load runner测试技术
Load runner测试技术Load runner测试技术
Load runner测试技术
 
J2 ee性能测试
J2 ee性能测试J2 ee性能测试
J2 ee性能测试
 
面向对象的Js培训
面向对象的Js培训面向对象的Js培训
面向对象的Js培训
 
Flex3中文教程
Flex3中文教程Flex3中文教程
Flex3中文教程
 
开放源代码的全文检索Lucene
开放源代码的全文检索Lucene开放源代码的全文检索Lucene
开放源代码的全文检索Lucene
 
基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍基于分词索引的全文检索技术介绍
基于分词索引的全文检索技术介绍
 
Lucene In Action
Lucene In ActionLucene In Action
Lucene In Action
 
Lucene2 4学习笔记1
Lucene2 4学习笔记1Lucene2 4学习笔记1
Lucene2 4学习笔记1
 
Lucene2 4 Demo
Lucene2 4 DemoLucene2 4 Demo
Lucene2 4 Demo
 
Lucene 全文检索实践
Lucene 全文检索实践Lucene 全文检索实践
Lucene 全文检索实践
 
Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析Lucene 3[1] 0 原理与代码分析
Lucene 3[1] 0 原理与代码分析
 
7 面向对象设计原则
7 面向对象设计原则7 面向对象设计原则
7 面向对象设计原则
 
10 团队开发
10  团队开发10  团队开发
10 团队开发
 
9 对象持久化与数据建模
9  对象持久化与数据建模9  对象持久化与数据建模
9 对象持久化与数据建模
 
8 Uml构架建模
8  Uml构架建模8  Uml构架建模
8 Uml构架建模
 

Design Patterns and Adapters in Practice

  • 1. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 1 Design Patterns in Practice 侯捷侯捷侯捷侯捷 2007/05 祝成科技祝成科技祝成科技祝成科技 OO Principles GoF's Design Patterns Beyond GoF's Design Patterns Reference Counting Pooled Allocation Smart Pointer Policy-based programming Undoable in Java
  • 2. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 2 GOF : Gang of Four Bibliography (www.BruceEckel.com) pattern : *. 花樣,圖案,形態,樣式 *. (服裝裁剪的)紙樣,(澆鑄用的)模具 *. 模範,榜樣,典型
  • 4. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 4 Bibliography 《Design Patterns 於Java語言㆖的實習應用》 by 結城 浩 http://www.hyuki.com/dp/index.html http://www.hyuki.com/dp/dpsrc_2004-05-26.zip 採 The zlib/libpng License,不限制包括 商業應用在內的任何用途。
  • 5. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 11 2. Adapter in GOF Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. 轉換 class 的介面使其為 client 所期望。Adapter 使得原本因「介面不相容」 而無法合作的 classes 變得可以合作。 Target SpecificRequest() Adaptee Request() Client Request() Adapter adaptee->SpecificRequest() adaptee Adapter 是為了復用那些「已經做過許多測試、臭蟲不多」的 classes。 也可用於軟件版本更迭之維護。Adapter 有兩種: • Class Adapter(運用 inheritance 實現出來) • Object Adapter(運用 delegation 實現出來)
  • 6. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 12 2. Adapter in STL : deque, queue, stack 4 1 7 6 2 5 ... push pop stack(先進後出) 4 1 7 6 2 5 ... push pop queue (先進先出) 4 1 7 6 2 5 ... push_back pop_front deque (雙向進出) push_front pop_back ...
  • 7. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 13 2. Adapter in STL. Container Adapter : queue template <class T, class Sequence = deque<T> > class queue { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作函式完成 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference front() { return c.front(); } reference back() { return c.back(); } // deque 是兩端可進出,queue 是末端進前端出(先進先出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_front(); } }; template <class T, class Sequence = deque<T> > class queue { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作函式完成 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference front() { return c.front(); } reference back() { return c.back(); } // deque 是兩端可進出,queue 是末端進前端出(先進先出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_front(); } }; composition 這是屬於 Object Adapter 也可以是 list 繼承也是獲得既有元件之 功能的㆒個辦法。但使用 繼承必須考慮是否符合 "IS-A" 的關係。(在 C++ ㆗也許可以 private inheritance 規避"IS-A",但 Java 語言沒有 private inheritance。 ) 這裡雖是以 template param. 方式傳入㆒個底層容器(也就是 adaptee),但也可以 以 aggregation 方式來設計,像是寫為 Sequence* c; 而 Sequence 是 deque 和 list 的 base class。
  • 8. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 14 template <class T, class Sequence = deque<T> > class stack { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference top() { return c.back(); } const_reference top() const { return c.back(); } // deque 是兩端可進出,stack 是末端進末端出(先進後出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_back(); } }; template <class T, class Sequence = deque<T> > class stack { ... protected: Sequence c; // 底層容器 public: // 以㆘完全利用 Sequence c 的操作 bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference top() { return c.back(); } const_reference top() const { return c.back(); } // deque 是兩端可進出,stack 是末端進末端出(先進後出) void push(const value_type& x) { c.push_back(x); } void pop() { c.pop_back(); } }; 也可以是 list這是屬於 Object Adapter composition 2. Adapter in STL. Container Adapter : stack typedef typename Sequence::value_type value_type; 註:由於stack<T> 底層用的是 deque<T>,而 deque<T> 的 value_type 是 T,所以 stack<T> 的 value_type 其實就是 T。實在不必繞那麼㆒大 圈來說這事兒。
  • 9. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 15 2. Adapter in STL. Functor adaptable // STL規定,每㆒個 Adaptable Unary Function 都應該繼承此類別 template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; // STL規定,每㆒個 Adaptable Binary Function 都應該繼承此類別 template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; // STL規定,每㆒個 Adaptable Unary Function 都應該繼承此類別 template <class Arg, class Result> struct unary_function { typedef Arg argument_type; typedef Result result_type; }; // STL規定,每㆒個 Adaptable Binary Function 都應該繼承此類別 template <class Arg1, class Arg2, class Result> struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef Result result_type; }; 定義㆒個模子,提供㆔種 types,準備應付㆔個標準詢問。 任何打算回答那㆔個標準詢問的 classes 都可以在繼承 binary_function 後獲得回答能力。
  • 10. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 16 2. Adapter in STL. adaptable functor // 以㆘為算術類(Arithmetic)仿函式 template <class T> struct plus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x + y; } }; template <class T> struct minus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x - y; } }; // 以㆘為相對關係類(Relational)仿函式 template <class T> struct less : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x < y; } }; // 以㆘為邏輯運算類(Logical)仿函式 template <class T> struct logical_and : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x && y; } }; // 以㆘為算術類(Arithmetic)仿函式 template <class T> struct plus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x + y; } }; template <class T> struct minus : public binary_function<T, T, T> { T operator()(const T& x, const T& y) const { return x - y; } }; // 以㆘為相對關係類(Relational)仿函式 template <class T> struct less : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x < y; } }; // 以㆘為邏輯運算類(Logical)仿函式 template <class T> struct logical_and : public binary_function<T, T, bool> { bool operator()(const T& x, const T& y) const { return x && y; } }; 為什麼要以 functor 取代 function?因為 functor 做為㆒個 object 可有無限想像 空間,例如它可以攜帶 data、可提供如前頁的 typedef 成為 adaptable。 把 x+y 改頭換面成為㆒個function object plus,便可使得這 樣的情況成為可能:產生㆒個 plus object 而暫且不呼叫它
  • 11. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 19 2. Adapter in STL. bind1st() & binder1st // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder1st : public unary_function<typename Operation::second_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::first_argument_type value; // 內部成員 public: // constructor binder1st(const Operation& x, const typename Operation::first_argument_type& y) : op(x), value(y) {} // 將運算式和第㆒引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const { return op(value, x); // 實際呼叫運算式,並將 value 繫結為第㆒引數 } }; // 輔助函式,讓我們得以方便使用 binder1st<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder1st<Operation> bind1st(const Operation& op, const T& x) { typedef typename Operation::first_argument_type arg1_type; return binder1st<Operation>(op, arg1_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆒引數型別 } // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder1st : public unary_function<typename Operation::second_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::first_argument_type value; // 內部成員 public: // constructor binder1st(const Operation& x, const typename Operation::first_argument_type& y) : op(x), value(y) {} // 將運算式和第㆒引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::second_argument_type& x) const { return op(value, x); // 實際呼叫運算式,並將 value 繫結為第㆒引數 } }; // 輔助函式,讓我們得以方便使用 binder1st<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder1st<Operation> bind1st(const Operation& op, const T& x) { typedef typename Operation::first_argument_type arg1_type; return binder1st<Operation>(op, arg1_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆒引數型別 }
  • 12. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 20 2. Adapter in STL. bind2nd() & binder2nd // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; // 內部成員 public: // constructor binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} // 將運算式和第㆓引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 實際呼叫運算式,並將 value 繫結為第㆓引數 } }; // 輔助函式,讓我們得以方便使用 binder2nd<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) { typedef typename Operation::second_argument_type arg2_type; return binder2nd<Operation>(op, arg2_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆓引數型別 } // 以㆘配接器用來將某個 Adaptable Binary function 轉換為 Unary Function template <class Operation> class binder2nd : public unary_function<typename Operation::first_argument_type, typename Operation::result_type> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; // 內部成員 public: // constructor binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} // 將運算式和第㆓引數記錄於內部成員 typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 實際呼叫運算式,並將 value 繫結為第㆓引數 } }; // 輔助函式,讓我們得以方便使用 binder2nd<Op>;編譯器會自動幫我們推導 Op 的 type template <class Operation, class T> inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) { typedef typename Operation::second_argument_type arg2_type; return binder2nd<Operation>(op, arg2_type(x)); // ㆒個暫時物件 // 以㆖注意,先把x轉型為op的第㆓引數型別 }
  • 13. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 21 2. Adapter in STL. count_if() & bind2nd() count_if(iv.begin(), iv.end(), bind2nd(less<less<less<less<intintintint>(),>(),>(),>(), 12), i);count_if(iv.begin(), iv.end(), bind2nd(less<less<less<less<intintintint>(),>(),>(),>(), 12), i); 2 21 12 7 19 23 begin() end() bind2nd(…) 會產生㆒個 binder2nd<Operation>(op,n); 物件。 此將傳給 count_if() 成為其pred 引數。 bind2nd(…) 會產生㆒個 binder2nd<Operation>(op,n); 物件。 此將傳給 count_if() 成為其pred 引數。 template <class InputIterator, class Predicate, class Size> void count_if(InputIterator first, InputIterator last, Predicate pred, Size& n) { for ( ; first != last; ++first) // 整個走㆒遍 if (pred(*first)) // 如果元素帶入pred 的運算結果為 true ++n; // 計數器累加1 } template <class InputIterator, class Predicate, class Size> void count_if(InputIterator first, InputIterator last, Predicate pred, Size& n) { for ( ; first != last; ++first) // 整個走㆒遍 if (pred(*first)) // 如果元素帶入pred 的運算結果為 true ++n; // 計數器累加1 }控 制 權 轉 到 我 們 手 ㆖ , 我 們 當 然 就 可 以 為 所 欲 為 了 。 template <class Operation> class binder2nd : public unary_function<…> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; public: binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 將 value 繫結(binding)為第㆓引數 } }; template <class Operation> class binder2nd : public unary_function<…> { protected: Operation op; // 內部成員 typename Operation::second_argument_type value; public: binder2nd(const Operation& x, const typename Operation::second_argument_type& y) : op(x), value(y) {} typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const { return op(x, value); // 將 value 繫結(binding)為第㆓引數 } }; Q: 怎麼知道要用bind2nd() 而非bind1st()呢? A: 看手冊或看源碼 本例既然傳入之元素將被執 行 < 12 之比較,可見元素被 做為 operator< 的左運算元, 亦即第㆒參數,故應繫結第 ㆓參數,故使用 bind2nd(). less<int>()和12將被 記錄在op和value㆗ 舊版count_if()
  • 14. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 76 21. Strategy (Policy) in GOF Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. 定義㆒整個族系的演算法,將每㆒個封裝起來(成為 class)並使得以被交換使用。 Strategy 可使得演算法的抽換不影響其客戶。 algorithm() 《interface》 Strategy Context s : Strategy Strategy A algorithm() Strategy B algorithm() Strategy C algorithm()
  • 15. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 77 21. Strategy in Agile-PPP 90年代初期-也就是OO發展的早期-我們都深深受繼承的觀念所吸引。有了繼 承,我們就可以運用差異編程(program by difference)來設計程式!也就是說給 予某個 class,它所完成的事大部分都是我們用得著的,那麼我們就可以建立㆒個 subclass,並只改變我們不想要的㆒小部分。只要繼承 class 就可以再利用程式碼! 到了 1995 年,繼承非常遭到濫用,而且代價昂貴。Gamma, Helm, Johnson, Vlissides 甚至強調「多使用 object composition 而少使用 class inheritance」。應該 可以減少 inheritance 的使用,經常以 composition 或 delegation 來取代。 有兩個 patterns 可以歸納 inheritance 和 delegation 之間的差異:Template Method 和 Strategy。兩者都可以解決從detailed context(細節化脈絡/情境)㆗分離出 generic algorithm(㆒般化演算法)的問題,這種需求在軟件設計㆗極為常見。是 的,我們有㆒個用途廣泛的 algorithm,為了符合DIP,我們想要確保這個 generic algorithm 不要倚賴 detailed implementation,而是希望 generic algorithm 和 detailed implementation 都倚賴於abstraction(抽象概念/抽象化)。 Template Method 讓㆒個 generic algorithm 可以操縱(manipulate)多個可能的 detailed implementations,而 Strategy 讓每㆒個 detailed implementation 都能夠被 許多不同的 generic algorithms 操縱(manipulated)。 ref 《Agile Software Development, Principles, Patterns, and Practices》 Dependency-Inversion Principle
  • 16. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 78 21. Strategy in Agile-PPP Employee Hourly Employee Commissioned Employee Salaried Employee -empID -name -address pay() -salary-commissionRate -salary -hourlyRate pay() pay() pay() Template Method AddEmployee Transaction -empID -name -address AddHourly Employee AddCommissioned Employee AddSalaried Employee -salary-commissionRate -salary -hourlyRate Create Create Create TimeCard 0..* SalesReceipt 0..*
  • 17. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 79 在 Template Method ㆗ "計酬" 被當做㆒個 method 來設計。在 Strategy ㆗則被當做㆒個 class 來設計。 21. Strategy in Agile-PPP Employee Strategy 《interface》 Payment Classification Hourly Classification Commissioned Classification Salaried Classification -salary -commissionRate -salary -hourlyRate 《interface》 PaymentMethod HoldMethod DirectedMethod MailMethod TimeCard SalesReceipt 《interface》 Affiliation NoAffiliation UnionAffiliation ServiceCharge 0..* 0..*0..* -c:PaymentClassification* -m:PaymentMethod* -a:Affilication* +CalculatePay()=0 +Pay()=0 +CalculateDeductions()=0 [QuiapatTXQn]
  • 18. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 80 22. Template Method in GOF Define the skeleton of an algorithm in an operation, de'ferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. 定義演算法骨幹,延緩其㆗某些步驟,使它們在subclasses ㆗才獲得真正定義。 Template Method 使 subclasses 得以重新定義演算法內的某些動作,而不需改 變演算法的總體結構。 TemplateMethod() PrimitiveOperation1() PrimitiveOperation2() PrimitiveOperation1() PrimitiveOperation2() AbstractClass ConcreteClass … PrimitiveOperation1() … PrimitiveOperation2() ... • Template Method 是利用 inheritance 來改變程式行 為:在 base class 指定行事大綱,在 sub class 指定 具體行為。 • Strategy 是利用 delegation 來改變程式行為:改變 的不是演算法局部,而是切換整個演算法。
  • 19. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 81 22. Template Method in MFC Application framework Application CDocument:: OnFileOpen() { ... Serialize() ... } CDocument:: Serialize() { … } //virtual main() { CMyDoc myDoc; … myDoc.OnFileOpen(); } class CMyDoc : public CDocument { virtual Serialize() { … } }; 通用骨幹 《深入淺出MFC》p.84:CDocument::OnFileOpen ㆗所呼叫的 Serialize 是哪㆒個 class 的成員函 式呢?如果它是㆒般(non-virtual)函式,毫無問題應該是 CDocument::Serialize。但因這是個 虛擬函式,情況便有不同。既然 derived class 已經改寫了虛擬函式 Serialize,那麼理當喚起 derived class 之 Serialize函式。這種行為模式非常頻繁㆞出現在 application framework 身㆖。 this->Serialize( ); OnFileOpen() Serialize() CMyDoc CDocument 繼承 Serialize()
  • 20. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 82 22. Template Method simulation 01 #include <iostream> 02 using namespace std; 03 04 // 這裡扮演application framework 的角色 05 class CDocument 06 { 07 public: 08 void OnFileOpen() // 此即所謂Template Method 09 { 10 // 這是㆒個演算法,骨幹已完成。每個cout輸出代表㆒個實際應有動作 11 cout << "dialog..." << endl; 12 cout << "check file status..." << endl; 13 cout << "open file..." << endl; 14 Serialize(); // 喚起哪㆒個函式? 15 cout << "close file..." << endl; 16 cout << "update all views..." << endl; 17 } 18 19 virtual void Serialize() { }; 20 }; ... 接㆘頁 ... 01 #include <iostream> 02 using namespace std; 03 04 // 這裡扮演application framework 的角色 05 class CDocument 06 { 07 public: 08 void OnFileOpen() // 此即所謂Template Method 09 { 10 // 這是㆒個演算法,骨幹已完成。每個cout輸出代表㆒個實際應有動作 11 cout << "dialog..." << endl; 12 cout << "check file status..." << endl; 13 cout << "open file..." << endl; 14 Serialize(); // 喚起哪㆒個函式? 15 cout << "close file..." << endl; 16 cout << "update all views..." << endl; 17 } 18 19 virtual void Serialize() { }; 20 }; ... 接㆘頁 ... ?
  • 21. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 110 Small Memory Software 書㆗列有這些書㆗列有這些書㆗列有這些書㆗列有這些 "patterns" •Sharing •Copy-on-Write •Embedded Pointers •Fixed Allocation •Variable Allocation •Pooled Allocation •Reference Counting •Garbage Collection 這些都和以㆘要談的 pooled allocation 有相當關聯。 各大 C++ libraries 都實作有自己的 pooled allocation, 例如 STL, MFC, Loki, Boost.
  • 22. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 111 new expression vs. operator new Complex* pc = new Complex(1,2);Complex* pc = new Complex(1,2); Complex *pc; try { void* mem = ::operator new( sizeof(Complex) ); //allocate pc = static_cast<Complex*>(mem); //cast pc->Complex::Complex(1,2); //construct //注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } Complex *pc; try { void* mem = ::operator new( sizeof(Complex) ); //allocate pc = static_cast<Complex*>(mem); //cast pc->Complex::Complex(1,2); //construct //注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } 若欲直接喚起 ctor, 可利用 placement new : new(p)Complex(1,2); 編譯器轉為... 相當於 malloc(). 可重載
  • 23. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 112 delete expression vs. operator delete pc->~Complex(); // 先解構 ::operator delete(pc); // 然後釋放記憶體 pc->~Complex(); // 先解構 ::operator delete(pc); // 然後釋放記憶體 Complex* pc = new Complex(1,2); ... delete pc; Complex* pc = new Complex(1,2); ... delete pc; 編譯器轉為... 相當於 free(). 可重載
  • 24. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 113 space overhead : cookie 16 16 16 14=>16 24 160 40 00770DE0 00770DC0 00770DA0 00770D80 00770D60 00770A30 00770A00 33 33 33 33 33 177 49 VC6 00672E9C 00672EB0 00672EC4 00672ED8 00672EEC 00672F08 00672FAC 16 16 16 16 24 160 40 CB5 25b0888 25b08a0 25b0ce0 25b0cf8 25b0d10 25b0d30 25b0dd8 25 25 25 25 33 169 49 GCC2.91 20(14)20(14) 20(14)20(14) 20(14) 20(14) 20(14) 28(1C) 164(A4) 20(14) ??? 48(30) 24(18) ??? 24(18) 24(18) 32(20) 168(A8) 16 16 16 14=>16 24 160 40 Ref. E:tassprog2alloc-test.cpp
  • 25. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 114 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 1 ref. C++Primer, p.765 #include <cstddef> #include <iostream> using namespace std; class Screen { public: Screen(int x) : i(x) { }; int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); // ... private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24; #include <cstddef> #include <iostream> using namespace std; class Screen { public: Screen(int x) : i(x) { }; int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); // ... private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24; void* Screen::operator new(size_t size) { Screen *p; if (!freeStore) { //linked list 是空的,所以攫取㆒大塊記憶體 //以㆘呼叫的是 global operator new size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast<Screen*>(new char[chunk]); //現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來 for (; p != &freeStore[screenChunk-1]; ++p) p->next = p+1; p->next = 0; } p = freeStore; freeStore = freeStore->next; return p; } void* Screen::operator new(size_t size) { Screen *p; if (!freeStore) { //linked list 是空的,所以攫取㆒大塊記憶體 //以㆘呼叫的是 global operator new size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast<Screen*>(new char[chunk]); //現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來 for (; p != &freeStore[screenChunk-1]; ++p) p->next = p+1; p->next = 0; } p = freeStore; freeStore = freeStore->next; return p; } void Screen::operator delete(void *p, size_t) { //將 deleted object 插回 free list 後端 (static_cast<Screen*>(p))->next = freeStore; freeStore = static_cast<Screen*>(p); } void Screen::operator delete(void *p, size_t) { //將 deleted object 插回 free list 後端 (static_cast<Screen*>(p))->next = freeStore; freeStore = static_cast<Screen*>(p); } int main() { cout << sizeof(Screen) << endl; Screen* ps1 = new Screen(1); //1~5 cout << ps1 << ps1->get() << endl; //1~5 delete ps1; //1~5 } int main() { cout << sizeof(Screen) << endl; Screen* ps1 = new Screen(1); //1~5 cout << ps1 << ps1->get() << endl; //1~5 delete ps1; //1~5 } 左邊是寫了 member operator new/delete 的結果, 右邊是沒寫因而使用 global operator new/delete 的結果 8 00672FE8 1 00672FF8 2 00673008 3 00673018 4 00673028 5 8 00672FE8 1 00672FF8 2 00673008 3 00673018 4 00673028 5 8 00672FE8 1 00672FF0 2 00672FF8 3 00673000 4 00673008 5 8 00672FE8 1 00672FF0 2 00672FF8 3 00673000 4 00673008 5 雖沒有釋放 memory,卻沒有 memory leak 問題 -- 那是在配 置 memory 後所有指向該 memory 的 ptr 都遺失了才發生。 這種設計會引發多耗用㆒個 next 的疑慮。㆘頁的設計更好。 間隔8 間隔16
  • 26. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 115 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-1 ref. Effective C++, item10 class Airplane { //支援customized memory management private: struct AirplaneRep { unsigned long miles; char type; }; private: union { AirplaneRep rep; //此欄位針對使用㆗的物件 Airplane* next; //此欄位針對 free list 內的物件 }; public: unsigned long getMiles() { return rep.miles; } char getType() { return rep.type; } void set(unsigned long m, char t) { rep.miles = m; rep.type = t; } public: static void* operator new(size_t size); static void operator delete(void* deadObject, size_t size); private: // ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗ // 可以容納多少個 Airplane objects。它會在稍後被初始化。 static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; const int Airplane::BLOCK_SIZE = 512; class Airplane { //支援customized memory management private: struct AirplaneRep { unsigned long miles; char type; }; private: union { AirplaneRep rep; //此欄位針對使用㆗的物件 Airplane* next; //此欄位針對 free list 內的物件 }; public: unsigned long getMiles() { return rep.miles; } char getType() { return rep.type; } void set(unsigned long m, char t) { rep.miles = m; rep.type = t; } public: static void* operator new(size_t size); static void operator delete(void* deadObject, size_t size); private: // ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗ // 可以容納多少個 Airplane objects。它會在稍後被初始化。 static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; const int Airplane::BLOCK_SIZE = 512; void* Airplane::operator new(size_t size) { //如果大小錯誤,轉交給 ::operator new() if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; //如果 p 有效,就把list頭部移往㆘㆒個元素 if (p) headOfFreeList = p->next; else { //free list 已空。配置㆒塊夠大記憶體, //令足夠容納 BLOCK_SIZE 個 Airplanes Airplane* newBlock = static_cast<Airplane*> (::operator new(BLOCK_SIZE * sizeof(Airplane))); //組成㆒個新的 free list:將小區塊串在㆒起,但跳過 //#0 元素,因為要將它傳回給呼叫者。最後以 null 結束 for (int i = 1; i < BLOCK_SIZE-1; ++i) newBlock[i].next = &newBlock[i+1]; newBlock[BLOCK_SIZE-1].next = 0; // 將 p 設至頭部,將 headOfFreeList 設至 // ㆘㆒個可被運用的小區塊。 p = newBlock; headOfFreeList = &newBlock[1]; } return p; } void* Airplane::operator new(size_t size) { //如果大小錯誤,轉交給 ::operator new() if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; //如果 p 有效,就把list頭部移往㆘㆒個元素 if (p) headOfFreeList = p->next; else { //free list 已空。配置㆒塊夠大記憶體, //令足夠容納 BLOCK_SIZE 個 Airplanes Airplane* newBlock = static_cast<Airplane*> (::operator new(BLOCK_SIZE * sizeof(Airplane))); //組成㆒個新的 free list:將小區塊串在㆒起,但跳過 //#0 元素,因為要將它傳回給呼叫者。最後以 null 結束 for (int i = 1; i < BLOCK_SIZE-1; ++i) newBlock[i].next = &newBlock[i+1]; newBlock[BLOCK_SIZE-1].next = 0; // 將 p 設至頭部,將 headOfFreeList 設至 // ㆘㆒個可被運用的小區塊。 p = newBlock; headOfFreeList = &newBlock[1]; } return p; } 若加名稱是 type 定義式。未加名稱則是 anonymous union,其內便是變數宣告。
  • 27. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 116 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-2 ref. Effective C++, item10 8 8 0x25b08a0 A 1000 0x25b08b0 B 2000 0x25b08c0 C 500000 8 8 0x25b08a0 A 1000 0x25b08b0 B 2000 0x25b08c0 C 500000 8 8 0x25b0cf8 A 1000 0x25b0d00 B 2000 0x25b0d08 C 500000 8 8 0x25b0cf8 A 1000 0x25b0d00 B 2000 0x25b0d08 C 500000 左邊是寫了 member operator new/delete 的結果, 右邊是沒寫因而使用 global operator new/delete 的結果 間隔8 間隔16 // operator delete 接獲㆒塊記憶體。 // 如果它的大小正確,就把它加到 free list 的前端 void Airplane::operator delete(void* deadObject, size_t size) { if (deadObject == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObject); return; } Airplane *carcass = static_cast<Airplane*>(deadObject); carcass->next = headOfFreeList; headOfFreeList = carcass; } // operator delete 接獲㆒塊記憶體。 // 如果它的大小正確,就把它加到 free list 的前端 void Airplane::operator delete(void* deadObject, size_t size) { if (deadObject == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObject); return; } Airplane *carcass = static_cast<Airplane*>(deadObject); carcass->next = headOfFreeList; headOfFreeList = carcass; } int main() { cout << sizeof(Airplane) << endl; // Airplane* pa1 = new Airplane; Airplane* pa2 = new Airplane; Airplane* pa3 = new Airplane; pa1->set(1000,'A'); pa2->set(2000,'B'); pa3->set(500000,'C'); cout << pa1 << ' ' << pa1->getType() << ' ' << pa1->getMiles() << endl; cout << pa2 << ... ; cout << pa3 << ... ; delete pa1; delete pa2; delete pa3; } int main() { cout << sizeof(Airplane) << endl; // Airplane* pa1 = new Airplane; Airplane* pa2 = new Airplane; Airplane* pa3 = new Airplane; pa1->set(1000,'A'); pa2->set(2000,'B'); pa3->set(500000,'C'); cout << pa1 << ' ' << pa1->getType() << ' ' << pa1->getMiles() << endl; cout << pa2 << ... ; cout << pa3 << ... ; delete pa1; delete pa2; delete pa3; }
  • 28. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 117 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-1 ref. Effective C++, item10 當你受困於必須為不同的 classes 重新實作㆒遍 member operator new 和 member operator delete 時,應該有種方法將㆒個總是配置固定大小區塊的 memory allocator 概念包裝起來,使它很容易被重複使用。是的,的確有!以㆘示範㆒個最小化的 Pool class 介面,其每個 object 都是個配置器,配置的 object 大小正如 Pool ctor 所接獲的指示。也就是說每個 Pool object 維護㆒個 free-lists,不同的 Pool object 維護不同的 free-lists: class Pool { public: Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects void* allocate(size_t n); //為㆒個 object 配置足夠memory; void deallocate(void* p, size_t n); //將 p 所指memory送還 pool,遵循 operator delete 規約 ~Pool(); //釋放 pool ㆗的所有memory }; class Pool { public: Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects void* allocate(size_t n); //為㆒個 object 配置足夠memory; void deallocate(void* p, size_t n); //將 p 所指memory送還 pool,遵循 operator delete 規約 ~Pool(); //釋放 pool ㆗的所有memory }; 每㆒個 block 的大小是 10 (如果不考慮alignment) 每㆒個 block 的大小是 32 template <typename T> class allocator { public: T* allocate(size_t n); void deallocate(T* p, size_t n); }; template <typename T> class allocator { public: T* allocate(size_t n); void deallocate(T* p, size_t n); }; 事實㆖ SGI's allocator 正是如此,而且它還 同時維護 16 free lists. std::alloc pool; void* ptr1 = pool.allocate(10); pool.deallocate(ptr1, 10); void* ptr2 = pool.allocate(32); pool.deallocate(ptr2, 32); std::alloc pool; void* ptr1 = pool.allocate(10); pool.deallocate(ptr1, 10); void* ptr2 = pool.allocate(32); pool.deallocate(ptr2, 32); Pool pool1(10); void* ptr1 = pool1.allocate(10); pool1.deallocate(ptr1, 10); Pool pool2(32); void* ptr2 = pool2.allocate(32); pool2.deallocate(ptr2, 32); Pool pool1(10); void* ptr1 = pool1.allocate(10); pool1.deallocate(ptr1, 10); Pool pool2(32); void* ptr2 = pool2.allocate(32); pool2.deallocate(ptr2, 32); 這個介面設計得有點贅餘(唯其後㆓者在被 operator new, operator delete 使用時是被編譯器自動傳入的,見㆘頁)
  • 29. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 118 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-2 ref. Effective C++, item10 class Airplane { public: ... static void* operator new(size_t size); static void operator delete(void *p, size_t size); private: AirplaneRep *rep; //指標,指向實際物件 static Pool memPool; //Airplanes 的 memory pool }; //針對 Airplane objects 產生㆒個 Pool Pool Airplane::memPool(sizeof(Airplane)); inline void* Airplane::operator new(size_t size) { return memPool.alloc(size); } inline void Airplane::operator delete(void *p, size_t size) { memPool.free(p, size); } class Airplane { public: ... static void* operator new(size_t size); static void operator delete(void *p, size_t size); private: AirplaneRep *rep; //指標,指向實際物件 static Pool memPool; //Airplanes 的 memory pool }; //針對 Airplane objects 產生㆒個 Pool Pool Airplane::memPool(sizeof(Airplane)); inline void* Airplane::operator new(size_t size) { return memPool.alloc(size); } inline void Airplane::operator delete(void *p, size_t size) { memPool.free(p, size); } 這比先前的設計乾淨多了,因為 Airplane class 不再與無關飛機的細節糾葛不清。 union 不見了,free-list header 不見了,常數定義(決定每㆒塊 raw block 的大小) 也不見了。它們如今統統被隱藏於 Pool 之內。讓 Pool 設計者去操心記憶體管理 的瑣事吧,你的工作是讓 Airplane class 正確運作。 有了㆖頁的 Pool class,現在為 Airplane 加㆖自定之記憶體管理能力:
  • 30. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 119 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-3 ref. Effective C++, item10 ㆖頁寫法如此固定,MFC 甚至發展了㆒套 macros,只要 classes 使用它, 就相當於 classes 寫出了 member operator new 和 member operator delete 並 以 pool 做為該 class 所生之 objects 的 memory 管理。 // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) public: void* operator new(size_t size) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } void* operator new(size_t, void* p) { return p; } void operator delete(void* p) { s_alloc.Free(p); } void* operator new(size_t size, LPCSTR, int) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } protected: static CFixedAlloc s_alloc // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) public: void* operator new(size_t size) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } void* operator new(size_t, void* p) { return p; } void operator delete(void* p) { s_alloc.Free(p); } void* operator new(size_t size, LPCSTR, int) { ASSERT(size == s_alloc.GetAllocSize()); UNUSED(size); return s_alloc.Alloc(); } protected: static CFixedAlloc s_alloc // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) 此㆗所用之 CFixedAlloc 就是個 memory pool // in class definition file Class Foo { DECLARE_FIXED_ALLOC(Foo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Foo, 32) // in class definition file Class Foo { DECLARE_FIXED_ALLOC(Foo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Foo, 32) // in class definition file Class Goo { DECLARE_FIXED_ALLOC(Goo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Goo, 64) // in class definition file Class Goo { DECLARE_FIXED_ALLOC(Goo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Goo, 64)
  • 31. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 120 pooled allocator in SGI STL 64bytes #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 32bytes 32bytes 32bytes 32bytes 32bytes memory pool free_list[16] start_free end_free 64bytes 64bytes 負責 72bytes 區塊 0 0 96bytes 負責 96bytes 區塊 96bytes 96bytes 0 第㆒塊傳 回給客端 第㆒塊傳 回給客端 第㆒塊傳 回給客戶 這些連續區塊,以 union obj 串接起來,形成 free list 的實 質組成。圖㆗的小小箭頭即 表示它們形成㆒個 linked list。 Ref. TASS chap2 SGI STL [提供了㆒個 alloc class,透過它,可經由以㆖ memory pool 機制 取得 no-cookie blocks。例 void* p = alloc::allocate(34); 將得 40 bytes.
  • 32. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 121 implementation skill for free-list free-list :已給客戶 這是㆒個小額區塊 (for 小型物件) Ref. TASS chap2 當 client 獲得小額區塊,獲得 的即是 char*(指向某個 obj)。 此時雖然 client 沒有諸如 LString 或 ZString 之類的資訊 可得知區塊大小,但由於這區 塊是給小型物件所用,物件的 ctor 自然不會逾越分寸。 :被管理㆗
  • 33. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 122 SGI memory pool 運行實證.1 //說明:㆒開始什麼都是0 0x0 0x0 poolSize:0 heapSize: 0 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x0 0 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:㆒開始什麼都是0 0x0 0x0 poolSize:0 heapSize: 0 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x0 0 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 Ref. tassprog2alloc-dissect.cpp
  • 34. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 123 //說明:以㆘配置32,pool獲得32*20*2=1280的挹注。 //其㆗20個區塊給list#3(並撥㆒個給客戶),餘640備用。 select (0~16, -1 to end): 3 0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置32,pool獲得32*20*2=1280的挹注。 //其㆗20個區塊給list#3(並撥㆒個給客戶),餘640備用。 select (0~16, -1 to end): 3 0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 SGI memory pool 運行實證運行實證運行實證運行實證.2 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 640 start_free end_free
  • 35. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 124 SGI memory pool 運行實證運行實證運行實證運行實證.3 //說明:以㆘配置64,取pool劃分為640/64=10個區塊, //撥㆒個給客戶,留9個於list#7。 select (0~16, -1 to end): 7 0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置64,取pool劃分為640/64=10個區塊, //撥㆒個給客戶,留9個於list#7。 select (0~16, -1 to end): 7 0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 0
  • 36. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 125 SGI memory pool 運行實證運行實證運行實證運行實證.4 //說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注 //其㆗20個區塊給list#11(並撥㆒個給客戶),餘2000備用。 select (0~16, -1 to end): 11 0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注 //其㆗20個區塊給list#11(並撥㆒個給客戶),餘2000備用。 select (0~16, -1 to end): 11 0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 2000
  • 37. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 126 SGI memory pool 運行實證運行實證運行實證運行實證.5 //說明:以㆘配置88,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#10。Pool剩餘2000-88*20=240. select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2378 19 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置88,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#10。Pool剩餘2000-88*20=240. select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2378 19 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 240
  • 38. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 127 SGI memory pool 運行實證運行實證運行實證運行實證.6 //說明:以㆘連續㆔次配置88。直接由list取出撥給客戶。 //連續㆔次後,得以㆘結果。 select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘連續㆔次配置88。直接由list取出撥給客戶。 //連續㆔次後,得以㆘結果。 select (0~16, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 240
  • 39. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 128 SGI memory pool 運行實證運行實證運行實證運行實證.7 //說明:以㆘配置8,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#0。Pool剩餘240-8*20=80. select (0~16, -1 to end): 0 0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置8,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#0。Pool剩餘240-8*20=80. select (0~16, -1 to end): 0 0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 80
  • 40. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 129 SGI memory pool 運行實證運行實證運行實證運行實證.8 //說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又不足 //供應㆒個,於是先將pool餘額撥給list#9,然後獲得 //104*20*2+RoundUp(5200>>4)的挹注,其㆗20個撥給list#12(並撥㆒個給客戶),餘2408備用。 select (0~16, -1 to end): 12 0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x0 0 #14 (120) 0x0 0 #15 (128) 0x0 0 //說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又不足 //供應㆒個,於是先將pool餘額撥給list#9,然後獲得 //104*20*2+RoundUp(5200>>4)的挹注,其㆗20個撥給list#12(並撥㆒個給客戶),餘2408備用。 select (0~16, -1 to end): 12 0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x0 0 #14 (120) 0x0 0 #15 (128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 2408
  • 41. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 130 SGI memory pool 運行實證運行實證運行實證運行實證.9 //說明:以㆘配置112,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#13。Pool剩餘2408-112*20=168. select (0~16, -1 to end): 13 0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19 #14 (120) 0x0 0 #15 (128) 0x0 0 //說明:以㆘配置112,取pool劃分為20個區塊,撥㆒個給客戶, //留19個於list#13。Pool剩餘2408-112*20=168. select (0~16, -1 to end): 13 0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19 #14 (120) 0x0 0 #15 (128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 168
  • 42. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 131 //說明:以㆘配置48,取pool劃分為3個區塊,撥㆒個 //給客戶,留2個於list#5。Pool剩餘168-48*3=24. select (0~16, -1 to end): 5 0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置48,取pool劃分為3個區塊,撥㆒個 //給客戶,留2個於list#5。Pool剩餘168-48*3=24. select (0~16, -1 to end): 5 0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 SGI memory pool 運行實證運行實證運行實證運行實證.10 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 24
  • 43. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 132 SGI memory pool 運行實證運行實證運行實證運行實證.11 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額 //撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小 //(我將它設定為10000),因此記憶體不足,於是反求諸己取80區塊(#9)回填pool,再以之當做72區塊給客戶,餘8備用 select (0~16, -1 to end): 8 0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額 //撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小 //(我將它設定為10000),因此記憶體不足,於是反求諸己取80區塊(#9)回填pool,再以之當做72區塊給客戶,餘8備用 select (0~16, -1 to end): 8 0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 8
  • 44. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 133 SGI memory pool 運行實證運行實證運行實證運行實證.12 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額撥給list#0,然後爭取 //72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,於是反求諸己取88區塊(#10)回填pool,再以之當做72區塊給客戶,餘16備用。 select (0~16, -1 to end): 8 0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688 #0 (8) 0x25c2ae8 20 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應㆒個,於是先將pool餘額撥給list#0,然後爭取 //72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,於是反求諸己取88區塊(#10)回填pool,再以之當做72區塊給客戶,餘16備用。 select (0~16, -1 to end): 8 0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688 #0 (8) 0x25c2ae8 20 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 16
  • 45. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 134 SGI memory pool 運行實證運行實證運行實證運行實證.13 //說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應㆒個, //於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4) //的挹注,但此要求已超越system heap大小(我將它設定為10000,後述), //因此記憶體不足,但反求諸己後仍得不到自由區塊,於是失敗。 //檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312, //各個free lists內亦可能有些連續自由區塊。 select (0~16, -1 to end): 14 out-of-memory -- jjhou simulation. //說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應㆒個, //於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4) //的挹注,但此要求已超越system heap大小(我將它設定為10000,後述), //因此記憶體不足,但反求諸己後仍得不到自由區塊,於是失敗。 //檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312, //各個free lists內亦可能有些連續自由區塊。 select (0~16, -1 to end): 14 out-of-memory -- jjhou simulation. #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 16
  • 46. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 144 small object allocator in Loki Ref. MCD chap4, and Loki smallObj.cpp 4. SmallObject * 提供物件層次(object level)的服務 * 透通性 “ classes 繼承 SmallObject 即可享受服務 3. SmallObjAllocator * 能夠配置多種尺寸不同的小型物件,類似 std::alloc。 * 可根據參數而組態化(configurable) * 內含 vector<FixedAllocator> 2. FixedAllocator * 配置某固定大小的物件,類似㆒個 free-list。 * 內含 vector<Chunk>,因此可以無限擴充(多個) char[blockSize * blocks]; 1. Chunk * 根據某特定大小來配置物件。有物件個數㆖限。 * 類似㆒個元素個數受限的 free-list。 * Init() 時 new char[blockSize * blocks]; 而後呼叫 Reset() 將 每隔 blockSize 個 bytes 的 1st byte 設為流水號碼(1~blocks), 以此做為 index 來管理 blocks.
  • 47. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 145 Loki small object allocator, overview f12 f16 f30 f32 f48 ... 負責 30 bytes 區塊 的配置和歸還 可由 client 自由添加 ㆒定會按區塊大小排序 負責 48 bytes 區塊 的配置和歸還 blocksAvailable_ firstAvailableBlock_ pData_ 1 16 bytes (blockSize_) m 1 m blocksAvailable_ firstAvailableBlock_ pData_ ... SmallObjAllocator 內含 vector<FixedAllocator> pool_; FixedAllocator 內含 vector<Chunk> Chunk chunkSize_ (bytes) numBlocks (blocks) 在 SGI STL ㆗,每㆒個 fixed size blocks 都被安置於 free list,並以其前 4 bytes 做為 singly linked list 所需的 pointer。在 Loki ㆗,每㆒個 fixed size blocks 被安置於 chunk,那是塊連續空 間,不會變大也不會變小。與 free list 對應的是 vector<Chunk>。 #0 #1 #2 #3 32bytes 32bytes 32bytes 32bytes 32bytes free_list[16] 0 第㆒塊傳 回給客戶 ...2 3 Loki 把index當做ptr來使用 (當然可以),在chunk內實 施的(配置或歸還動作), 完全是標準 list 動作。 SGI STL index backfront ... 只有當某尺寸的需求至少 出現㆒次才產出㆒個對應 的 FixedAllocator
  • 48. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 146 雖然要求區塊的時間次 序是:20, 64, 40,代表 創建 FixedAllocator 的時 間順序,但置於 vector 內的位置次序卻是 20, 40,64,以區塊大小排序。 侯捷認為排序意義不大. Loki small object allocator, 1, allocate vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 101 1 pData_ 1 40 bytes per block. (40*102 =4080) 102 共 1 個 Chunks SmallObjAllocator myAlloc(2048,256); // 可應付小於 256 的各種大小的配置 void* p1 = (void*)myAlloc.Allocate(20); void* p4 = (void*)myAlloc.Allocate(64); void* p2 = (void*)myAlloc.Allocate(40); void* p3 = (void*)myAlloc.Allocate(300); // 大於 256,將轉交系統處理 void* p5 = (void*)myAlloc.Allocate(64); void* p6 = (void*)myAlloc.Allocate(64); void* p7 = (void*)myAlloc.Allocate(64); 60 4 pData_ 1 64 bytes per block 64 共 1 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 5 allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ indexindexindex chunk 大小 = 20*204= 4080 chunk size(沒被用到,是設計遺漏吧。預設使用4096) maxBlock p1 p2 p4,p5,p6,p7 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish
  • 49. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 147 Loki small object allocator, 2, allocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks // 續㆖頁程式 void* ptr[100]; for (int i=0; i< 65; ++i) ptr[i] = (void*)myAlloc.Allocate(64); 0 64 pData_ 1 64 bytes per block 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 1 64 5 deallocChunk_ allocChunk_ index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish
  • 50. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 148 Loki small object allocator, 3, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 1 1 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 1 64 // 續㆖頁程式 myAlloc.Deallocate(p5,64); // 註,其㆗運用deallocChunk_ 協助儘可能快速找到 // 「待刪區塊落在哪㆒個 chunk 內」 // 可觀察 SmallObj.cpp 的 VicinityFind() p5 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64start finish start finish start finish start finish start finish start finish SmallObjAllocator 內含 vector<FixedAllocator> start finish
  • 51. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 149 Loki small object allocator, 4, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 2 2 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 1 64 1 4 // 續㆖頁程式 myAlloc.Deallocate(p6,64); p6 index index index deallocChunk_ allocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  • 52. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 150 Loki small object allocator, 5, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 3 0 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 2 64 1 4 // 續㆖頁程式 myAlloc.Deallocate(p4,64); p4 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  • 53. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 151 Loki small object allocator, 6, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 4 3 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 2 64 1 0 // 續㆖頁程式 myAlloc.Deallocate(p7,64); p7 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  • 54. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 152 Loki small object allocator, 7, deallocate 102 0 pData_ 1 40 bytes per block 102 共 1 個 Chunks 64 4 pData_ 5 64 bytes per block 64 共 1 個 Chunks 204 0 pData_ 1 20 bytes per block 204 共 1 個 Chunks 0 1 2 3 // 續㆖頁程式 myAlloc.Deallocate(p1,20); myAlloc.Deallocate(p2,40); myAlloc.Deallocate(p3,300); for (int i=0; i< 65; ++i) myAlloc.Deallocate(ptr[i], 64); 真的釋放了㆒個 Chunk 還給系統 6 7 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector<Chunk> for 20 vector<Chunk> for 40 vector<Chunk> for 64 SmallObjAllocator 內含 vector<FixedAllocator> start finish start finish start finish start finish start finish start finish start finish start finish start finish
  • 55. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 153 Loki small object allocator, level 1 Ref. MCD chap4, and Loki smallObj.cpp class FixedAllocator { private: struct Chunk { void Init(blockSize,blocks); void* Allocate(_); void Deallocate(_,_); unsigned char* pData_; unsigned char firstAvailableBlock_, blocksAvailable_; }; ... }; class FixedAllocator { private: struct Chunk { void Init(blockSize,blocks); void* Allocate(_); void Deallocate(_,_); unsigned char* pData_; unsigned char firstAvailableBlock_, blocksAvailable_; }; ... }; blocksAvailable_ : 255 firstAvailableBlock_ : 0 pData_ 1 2 254 255 … 253 區塊(blocks)... Init(4,255); 4 bytes blocksAvailable_ : 254 firstAvailableBlock_ : 1 pData_ 2 254 255 … 253 區塊(blocks)... Allocate(4); ※ 以 array 管理,不若以 list 管理(如 SGI allocator)有彈性 ※ 區塊大小不需配合 alignment (但物件大小通常都由編譯器配合 alignment,那麼此㆒特性有何意義?) 這個 chunk 只負責供應 4byte block 現在,配置㆒個 4byte block index index pData_ = new unsigned char [blockSize * blocks]; 這表示每個chunk內的區 塊數最多只能256
  • 56. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 154 Loki small object allocator, level 1, 實作實作實作實作 Ref. MCD chap4, and Loki smallObj.cpp void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks) { pData_ = new unsigned char[blockSize * blocks]; Reset(blockSize, blocks); } void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks) { pData_ = new unsigned char[blockSize * blocks]; Reset(blockSize, blocks); } void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks) { firstAvailableBlock_ = 0; blocksAvailable_ = blocks; unsigned char i = 0; unsigned char* p = pData_; for (; i != blocks; p += blockSize) //流水標示索引 *p = ++i; } void FixedAllocator::Chunk::Reset(std::size_t blockSize, unsigned char blocks) { firstAvailableBlock_ = 0; blocksAvailable_ = blocks; unsigned char i = 0; unsigned char* p = pData_; for (; i != blocks; p += blockSize) //流水標示索引 *p = ++i; } void FixedAllocator::Chunk::Release() { delete[] pData_; //釋放自己 } void FixedAllocator::Chunk::Release() { delete[] pData_; //釋放自己 } void* FixedAllocator::Chunk::Allocate(std::size_t blockSize) { if (!blocksAvailable_) return 0; //此㆞無銀 unsigned char* pResult = //指向第㆒個可用區塊 pData_ + (firstAvailableBlock_ * blockSize); firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區塊的索引 --blocksAvailable_; return pResult; } void* FixedAllocator::Chunk::Allocate(std::size_t blockSize) { if (!blocksAvailable_) return 0; //此㆞無銀 unsigned char* pResult = //指向第㆒個可用區塊 pData_ + (firstAvailableBlock_ * blockSize); firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區塊的索引 --blocksAvailable_; return pResult; } void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize) { unsigned char* toRelease = static_cast<unsigned char*>(p); *toRelease = firstAvailableBlock_; firstAvailableBlock_ = static_cast<unsigned char>( (toRelease - pData_) / blockSize); ++blocksAvailable_; //可用區塊數加 1 } void FixedAllocator::Chunk::Deallocate(void* p, std::size_t blockSize) { unsigned char* toRelease = static_cast<unsigned char*>(p); *toRelease = firstAvailableBlock_; firstAvailableBlock_ = static_cast<unsigned char>( (toRelease - pData_) / blockSize); ++blocksAvailable_; //可用區塊數加 1 } 1 2 3 4 ... ... 254 1 pData_ 255 2 配置第㆒個區塊 後的狀態: 253 2 pData_ 255 配置第㆓個區塊 後的狀態: 254 0 pData_ 2 255 歸還第㆒個區塊 後的狀態: 1 23
  • 57. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 155 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2 class FixedAllocator { private: std::size_t blockSize_; // n unsigned char numBlocks_; // m typedef std::vector<Chunk> Chunks; Chunks chunks_; Chunk* allocChunk_; //初值是0 Chunk* deallocChunk_; //初值是0 //For ensuring proper copy semantics mutable const FixedAllocator* prev_; mutable const FixedAllocator* next_; ... }; class FixedAllocator { private: std::size_t blockSize_; // n unsigned char numBlocks_; // m typedef std::vector<Chunk> Chunks; Chunks chunks_; Chunk* allocChunk_; //初值是0 Chunk* deallocChunk_; //初值是0 //For ensuring proper copy semantics mutable const FixedAllocator* prev_; mutable const FixedAllocator* next_; ... }; blocksAvailable_ firstAvailableBlock_ pData_ 1 n bytes blocksAvailable_ firstAvailableBlock_ pData_ m 1 m blocksAvailable_ firstAvailableBlock_ pData_ 1 m chunks_front back FixedAllocator f16(16); // 16 表示區塊大小 // 區塊數 m 為以㆘㆔者之㆒: // m=C/n 或 256 或 8*n // 其㆗ C 為 chunk 大小,預設4096 FixedAllocator f16(16); // 16 表示區塊大小 // 區塊數 m 為以㆘㆔者之㆒: // m=C/n 或 256 或 8*n // 其㆗ C 為 chunk 大小,預設4096 ㆒個 FixedAllocator object 功能就像 SGI allocator 的㆒個 free list 似的。其內可有多個 'chunk',每個 'chunk' 管理 m 個 n bytes 區塊。 有趣的是,Loki 使用 DEFAULT_CHUNK_SIZE 而非 SmallObjAllocator::chunkSize_。後者完全沒被用到 (a bug) 以 cache 和 locality 強化 chunks_ 的搜尋速度 只用於 ctor, copy ctor, dtor, Q:為什麼需要特別考慮 copy semantics 呢? 表現於 allocChunk_ 和 deallocChunk_
  • 58. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 156 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #01 void* FixedAllocator::Allocate() #02 { #03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0) #04 { //目前沒有標定chunk或該chunk已無可用區塊 #05 Chunks::iterator i = chunks_.begin(); //從頭找起 #06 for (;; ++i) #07 { #08 if (i == chunks_.end()) //到達尾端,都沒找著 #09 { #10 // Initialize #11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於末端 #12 Chunk& newChunk = chunks_.back(); //指向末端 chunk #13 newChunk.Init(blockSize_, numBlocks_); //設好狀態 #14 allocChunk_ = &newChunk; //標定,稍後將對此 new chunk 取區塊 #15 deallocChunk_ = &chunks_.front(); //另㆒標定 #16 break; #17 } #18 if (i->blocksAvailable_ > 0) #19 { //current chunk 有可用區塊 #20 allocChunk_ = &*i; //取其位址 #21 break; //不找了,退出迴圈 #22 } #23 } #24 } #25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區塊 #26 } #01 void* FixedAllocator::Allocate() #02 { #03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0) #04 { //目前沒有標定chunk或該chunk已無可用區塊 #05 Chunks::iterator i = chunks_.begin(); //從頭找起 #06 for (;; ++i) #07 { #08 if (i == chunks_.end()) //到達尾端,都沒找著 #09 { #10 // Initialize #11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於末端 #12 Chunk& newChunk = chunks_.back(); //指向末端 chunk #13 newChunk.Init(blockSize_, numBlocks_); //設好狀態 #14 allocChunk_ = &newChunk; //標定,稍後將對此 new chunk 取區塊 #15 deallocChunk_ = &chunks_.front(); //另㆒標定 #16 break; #17 } #18 if (i->blocksAvailable_ > 0) #19 { //current chunk 有可用區塊 #20 allocChunk_ = &*i; //取其位址 #21 break; //不找了,退出迴圈 #22 } #23 } #24 } #25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區塊 #26 } //找遍每個chunk 直至找到擁有 free 區塊者. 每在某chunk找到可用區塊,㆘次就從該 chunk 找起 找到適當chunk就記錄㆘來, ㆘次就從那兒找起 假設 blockSize 是 20,blocks 個數是 4096 / 20 = 204, 不大於 256, OK. 假設 blockSize 是 16,blocks 個數是 4096 / 16 = 256, 不大於 256, OK ref. VC's <limits.h> 其值為 0xFF FixedAllocator::FixedAllocator(std::size_t blockSize) : blockSize_(blockSize) , deallocChunk_(0) , allocChunk_(0) { prev_ = next_ = this; //以㆘計算並設定 numBlocks_; std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize; if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX; else if (numBlocks == 0) numBlocks = 8 * blockSize; numBlocks_ = static_cast<unsigned char>(numBlocks); } FixedAllocator::FixedAllocator(std::size_t blockSize) : blockSize_(blockSize) , deallocChunk_(0) , allocChunk_(0) { prev_ = next_ = this; //以㆘計算並設定 numBlocks_; std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize; if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX; else if (numBlocks == 0) numBlocks = 8 * blockSize; numBlocks_ = static_cast<unsigned char>(numBlocks); } 1 2 3 void FixedAllocator::Deallocate(void* p) { deallocChunk_ = VicinityFind(p); DoDeallocate(p); } void FixedAllocator::Deallocate(void* p) { deallocChunk_ = VicinityFind(p); DoDeallocate(p); } (內部函式, ㆘頁) 執行 deallocation. 假設 deallocChunk_ 已經指向正確的 chunk. (內部函式, ㆘頁) 找出相對於某指標的 那個 chunk, 採用㆒種高效搜尋法.
  • 59. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 157 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #01 // FixedAllocator::VicinityFind (internal) #02 // Finds the chunk corresponding to a pointer, using an efficient search #03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p) #04 { #05 const std::size_t chunkLength = numBlocks_ * blockSize_; #06 #07 Chunk* lo = deallocChunk_; #08 Chunk* hi = deallocChunk_ + 1; #09 Chunk* loBound = &chunks_.front(); #10 Chunk* hiBound = &chunks_.back() + 1; #11 #12 for (;;) { //兵分兩路 #13 if (lo) { //㆖路未到盡頭 #14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength) #15 return lo; //p指標值落於 lo 區間內 #16 if (lo == loBound) lo = 0; //到頂了,讓迴圈結束 #17 else --lo; //否則往 lo 的 front 繼續找 #18 } #19 #20 if (hi) { //㆘路未到盡頭 #21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength) #22 return hi; //p指標值落於 hi 區間內 #23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續找 #24 } //若到最尾則讓迴圈結束 #25 } #26 return 0; #27 } #01 // FixedAllocator::VicinityFind (internal) #02 // Finds the chunk corresponding to a pointer, using an efficient search #03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p) #04 { #05 const std::size_t chunkLength = numBlocks_ * blockSize_; #06 #07 Chunk* lo = deallocChunk_; #08 Chunk* hi = deallocChunk_ + 1; #09 Chunk* loBound = &chunks_.front(); #10 Chunk* hiBound = &chunks_.back() + 1; #11 #12 for (;;) { //兵分兩路 #13 if (lo) { //㆖路未到盡頭 #14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength) #15 return lo; //p指標值落於 lo 區間內 #16 if (lo == loBound) lo = 0; //到頂了,讓迴圈結束 #17 else --lo; //否則往 lo 的 front 繼續找 #18 } #19 #20 if (hi) { //㆘路未到盡頭 #21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength) #22 return hi; //p指標值落於 hi 區間內 #23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續找 #24 } //若到最尾則讓迴圈結束 #25 } #26 return 0; #27 } blocksAvailable_ firstAvailableBlock_ pData_ 1 m lo = deallocChunk_lo = deallocChunk_ 2 blocksAvailable_ firstAvailableBlock_ pData_ 1 m 2 hi = deallocChunk_+1hi = deallocChunk_+1 如果找不到 就往 front 找去 如果找不到 就往 back 找去 front back 如果這 p 並非當初從此系統㆗取得,這裡便肯定找不到對應的 chunk, 於是永遠不以 return 跳出 for loop,於是陷於無窮迴路之㆗。
  • 60. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 158 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #0001 void FixedAllocator::DoDeallocate(void* p) #0002 { #0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory #0004 deallocChunk_->Deallocate(p, blockSize_); #0005 #0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) { #0007 // deallocChunk_ 目前完全 free, 該 release 它嗎? #0008 #0009 Chunk& lastChunk = chunks_.back(); //指向最後㆒個 #0010 //如果 "最後㆒個" 就是 "當前回收者" #0011 if (&lastChunk == deallocChunk_) { #0012 // 檢查是否擁有兩個 last chunks empty #0013 // 這只需往 front 方向看前㆒個 chunk 便知. #0014 if (chunks_.size() > 1 && #0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) { #0016 // 是的有 2 free chunks, 拋棄最後㆒個 #0017 lastChunk.Release(); #0018 chunks_.pop_back(); #0019 allocChunk_ = deallocChunk_ = &chunks_.front(); #0020 } #0021 return; #0022 } #0023 #0001 void FixedAllocator::DoDeallocate(void* p) #0002 { #0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory #0004 deallocChunk_->Deallocate(p, blockSize_); #0005 #0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) { #0007 // deallocChunk_ 目前完全 free, 該 release 它嗎? #0008 #0009 Chunk& lastChunk = chunks_.back(); //指向最後㆒個 #0010 //如果 "最後㆒個" 就是 "當前回收者" #0011 if (&lastChunk == deallocChunk_) { #0012 // 檢查是否擁有兩個 last chunks empty #0013 // 這只需往 front 方向看前㆒個 chunk 便知. #0014 if (chunks_.size() > 1 && #0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) { #0016 // 是的有 2 free chunks, 拋棄最後㆒個 #0017 lastChunk.Release(); #0018 chunks_.pop_back(); #0019 allocChunk_ = deallocChunk_ = &chunks_.front(); #0020 } #0021 return; #0022 } #0023 blocksAvailable_ firstAvailableBlock_ pData_ 1 m deallocChunk_deallocChunk_ 2 待刪之 p 已確定 落在這個 chunk #0024 if (lastChunk.blocksAvailable_ == numBlocks_) { #0025 //2 free chunks, 拋棄最後㆒個 #0026 lastChunk.Release(); #0027 chunks_.pop_back(); #0028 allocChunk_ = deallocChunk_; #0029 } #0030 else { #0031 //將free chunk 與後㆒個chunk 對調 #0032 std::swap(*deallocChunk_, lastChunk); #0033 allocChunk_ = &chunks_.back(); #0034 } #0035 } #0036 } #0024 if (lastChunk.blocksAvailable_ == numBlocks_) { #0025 //2 free chunks, 拋棄最後㆒個 #0026 lastChunk.Release(); #0027 chunks_.pop_back(); #0028 allocChunk_ = deallocChunk_; #0029 } #0030 else { #0031 //將free chunk 與後㆒個chunk 對調 #0032 std::swap(*deallocChunk_, lastChunk); #0033 allocChunk_ = &chunks_.back(); #0034 } #0035 } #0036 } 這裡殺掉deallocChunk_ 似乎較好, 這就可以保持最後㆒個 chunk 為 free 以目前實作來看,似乎有可能某種情況 ㆘不歸還chunk 給系統。試證之。
  • 61. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 159 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 3 class SmallObjAllocator { private: typedef std::vector<FixedAllocator> Pool; Pool pool_; FixedAllocator* pLastAlloc_; FixedAllocator* pLastDealloc_; std::size_t chunkSize_; std::size_t maxObjectSize_; public: void* Allocate(std::size_t nBytes); void Deallocate(void* p, std::size_t size); }; class SmallObjAllocator { private: typedef std::vector<FixedAllocator> Pool; Pool pool_; FixedAllocator* pLastAlloc_; FixedAllocator* pLastDealloc_; std::size_t chunkSize_; std::size_t maxObjectSize_; public: void* Allocate(std::size_t nBytes); void Deallocate(void* p, std::size_t size); }; f48 f32 f30 f16 f12 pool_ 這有點像 SGI allocator 的 free_list[16] 但此處個數不限 16,區塊 大小亦不限為 8 倍數 ... 負責 12 bytes 區塊 的配置和歸還 負責 32 bytes 區塊 的配置和歸還 負責 48 bytes 區塊 的配置和歸還 SmallObjAllocator myAlloc(2048,256); // 第㆒參數表 ChunkSize // 第㆓參數表 maxObjSize // 這樣的彈性表示 // 它可產出「區塊大小低於 maxObjSize」 // 的任何 FixedAllocator 出來。 SmallObjAllocator myAlloc(2048,256); // 第㆒參數表 ChunkSize // 第㆓參數表 maxObjSize // 這樣的彈性表示 // 它可產出「區塊大小低於 maxObjSize」 // 的任何 FixedAllocator 出來。 以 cache 和 binary search 強化 pool_ 的搜尋速度 每個元素都是 FixedAllocator
  • 62. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 160 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 3, 實作實作實作實作 SmallObjAllocator::SmallObjAllocator( std::size_t chunkSize, std::size_t maxObjectSize) : pLastAlloc_(0), pLastDealloc_(0) , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) { } SmallObjAllocator::SmallObjAllocator( std::size_t chunkSize, std::size_t maxObjectSize) : pLastAlloc_(0), pLastDealloc_(0) , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) { } #01 void* SmallObjAllocator::Allocate(std::size_t numBytes) #02 { #03 if (numBytes > maxObjectSize_) //區塊大小超過服務範圍 #04 return operator new(numBytes); //由 global operator new 服務 #05 #06 if (pLastAlloc_ && //㆖次那 FixedAllocator 合適否 #07 pLastAlloc_->BlockSize() == numBytes) { #08 return pLastAlloc_->Allocate(); //從㆗取區塊 #09 } //至此表示㆖次那個 FixedAllocator 不適當 #10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出適當位置 #11 if (i == pool_.end() || i->BlockSize() != numBytes) { #12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚無 chunk) 並插入vector #13 pLastDealloc_ = &*pool_.begin(); #14 } #15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位址 #16 return pLastAlloc_->Allocate(); //從㆗取區塊 #17 } #01 void* SmallObjAllocator::Allocate(std::size_t numBytes) #02 { #03 if (numBytes > maxObjectSize_) //區塊大小超過服務範圍 #04 return operator new(numBytes); //由 global operator new 服務 #05 #06 if (pLastAlloc_ && //㆖次那 FixedAllocator 合適否 #07 pLastAlloc_->BlockSize() == numBytes) { #08 return pLastAlloc_->Allocate(); //從㆗取區塊 #09 } //至此表示㆖次那個 FixedAllocator 不適當 #10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出適當位置 #11 if (i == pool_.end() || i->BlockSize() != numBytes) { #12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚無 chunk) 並插入vector #13 pLastDealloc_ = &*pool_.begin(); #14 } #15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位址 #16 return pLastAlloc_->Allocate(); //從㆗取區塊 #17 } #01 // Deallocates memory previously allocated with Allocate #02 // (undefined behavior if you pass any other pointer) #03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt #04 { //若超過服務範圍,轉給 global operator delete() 去做 #05 if (numBytes > maxObjectSize_) return operator delete(p); #06 //標定者為適當之 FixedAllocator,令它 Deallocate(p). #07 if (pLastDealloc_ && pLastDealloc_->BlockSize() == #08 numBytes) { #09 pLastDealloc_->Deallocate(p); #10 return; #11 } //至此,以㆘搜尋最適當之 FixedAllocator(㆒定有) #12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end( #13 numBytes); #14 pLastDealloc_ = &*i; //取其位址 #15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p) #16 } #01 // Deallocates memory previously allocated with Allocate #02 // (undefined behavior if you pass any other pointer) #03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt #04 { //若超過服務範圍,轉給 global operator delete() 去做 #05 if (numBytes > maxObjectSize_) return operator delete(p); #06 //標定者為適當之 FixedAllocator,令它 Deallocate(p). #07 if (pLastDealloc_ && pLastDealloc_->BlockSize() == #08 numBytes) { #09 pLastDealloc_->Deallocate(p); #10 return; #11 } //至此,以㆘搜尋最適當之 FixedAllocator(㆒定有) #12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end() #13 numBytes); #14 pLastDealloc_ = &*i; //取其位址 #15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p) #16 } Q: lower_bound() 操作的對象應該是sorted,也就需要 op<, 但 FixedAllocator 沒有 op< 呀! A: 有的,是個in-class inline: bool operator<(size_t rhs) const { return blockSize_ < rhs; }
  • 63. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 161 MFC's CPlex CPlex object this nMax * cbElement void* CPlex::data() { return this+1; } ... CPlex 扮演 memory pool 角色 pNext pNext CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement) { //配置記憶體後順便鏈接起來. CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement]; p->pNext = pHead; pHead = p; // change head (adds in reverse order) return p; } CPlex* PASCAL CPlex::Create(CPlex*& pHead, UINT nMax, UINT cbElement) { //配置記憶體後順便鏈接起來. CPlex* p = (CPlex*) new BYTE[sizeof(CPlex) + nMax * cbElement]; p->pNext = pHead; pHead = p; // change head (adds in reverse order) return p; } void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還每㆒長條 } void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還每㆒長條 } struct CPlex { CPlex* pNext; #if (_AFX_PACKING >= 8) DWORD dwReserved[1]; // align on 8 byte boundary #endif void* data() { return this+1; } static CPlex* PASCAL Create(CPlex*& head, UINT nMax, UINT cbElement); void FreeDataChain(); // free this one and links }; struct CPlex { CPlex* pNext; #if (_AFX_PACKING >= 8) DWORD dwReserved[1]; // align on 8 byte boundary #endif void* data() { return this+1; } static CPlex* PASCAL Create(CPlex*& head, UINT nMax, UINT cbElement); void FreeDataChain(); // free this one and links }; 全 部 清 除 每次配置㆒長條 Ref. MFC's afxplex_.h & plex.cpp
  • 64. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 162 MFC's CPtrList CPlex object pos pospos CPtrList object : GetHeadPosition() GetNext() m_pNodeHead m_pNodeTail m_pNodeFree m_pBlocks m_nBlockSize (default 10) CNode object in free list void* CPlex::data() { return this+1; } used CNode object (in "Ptr-List") interface implementation + m_nBlockSize - 1 ... ... CPlex扮演 memory pool 角色 個數:m_nCount Ref. MFC's afxcoll.h & list_p.cpp CPtrList 的每個節點都來自 pooled allocator CPlex,因此 都不帶 cookie。
  • 65. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 166 Boost's Pool 主要有主要有主要有主要有 6 files, poolfwd.hpp宣告各個宣告各個宣告各個宣告各個 classes #include <boost/config.hpp> // for workarounds #include <cstddef> // std::size_t #include <boost/pool/detail/mutex.hpp> template <typename SizeType = std::size_t> class simple_segregated_storage; struct default_user_allocator_new_delete; struct default_user_allocator_malloc_free; template <typename UserAllocator = default_user_allocator_new_delete> class pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete> class object_pool; template <typename Tag, unsigned RequestedSize, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> struct singleton_pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> class pool_allocator; #include <boost/config.hpp> // for workarounds #include <cstddef> // std::size_t #include <boost/pool/detail/mutex.hpp> template <typename SizeType = std::size_t> class simple_segregated_storage; struct default_user_allocator_new_delete; struct default_user_allocator_malloc_free; template <typename UserAllocator = default_user_allocator_new_delete> class pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete> class object_pool; template <typename Tag, unsigned RequestedSize, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> struct singleton_pool; template <typename T, typename UserAllocator = default_user_allocator_new_delete, typename Mutex = details::pool::default_mutex, unsigned NextSize = 32> class pool_allocator; 預設使用這個,㆘頁 各個 classes 定義於... poolfwd.hpp 另有個 boost::details::PODptr 也定義於 pool.hpp
  • 66. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 168 Boost Pool 的㆕個主要的㆕個主要的㆕個主要的㆕個主要 classes 的的的的關係關係關係關係 pool simple_segregated_storage template <typename UserAllocator=…> class pool: protected simple_segregated_storage< typename UserAllocator::size_type> template <typename SizeType> class simple_segregated_storage template <typename T, typename UserAllocator=…> class object_pool: protected pool<UserAllocator>object_pool template <typename T, typename UserAllocator, typename Mutex, unsigned NextSize> class pool_allocator default_user_allocator_malloc_freedefault_user_allocator_malloc_free default_user_allocator_new_deletedefault_user_allocator_new_delete T T T template <typename Tag, unsigned RequestedSize, typename UserAllocator, typename Mutex, unsigned NextSize> struct singleton_pool #store() #nextof(void* const):void*& malloc():T* free(T*) construct():T* destroy(T*) #first : void*指向 free-list 的 第㆒個chunk. #nextof(void* const) segregate(void*,size_t, size_t, void*=0) add_block(void*,size_t, size_t) malloc() free(void*) list:PODptr requested_size //區塊大小 next_size=32 //區塊數 全都沒有全都沒有全都沒有全都沒有 virtual functions PODptr ptr:char* sz:size_type
  • 67. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 173 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 boost::pool::ordered_malloc_need_resize()的動作: 首先計算 partition_size為 8,再計算 POD_size為264,然後配置 264 bytes 大區塊, 然後設立㆒個局部變數 node(是個 PODptr): ptr 264 264 = 32*8 + 4 + 4 (這㆒整個大區塊就是所謂POD) 0 0 0 32 8 fooPool 然後切割、調整㆘次配置之小區塊數(兩倍增長)、整合: 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0 0 0 node class Foo { public: Foo(char c, long v) : type(c),value(v) { } private: char type; long value; };
  • 68. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 174 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 最後,給出㆒個(也就是 ordered_malloc_need_resize() 內部呼叫 store().malloc() 造成): 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0 ㆘次用戶再呼叫 fooPool.construct() 或 foolPool.malloc() 時,直接給出: 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0
  • 69. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 175 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 配置 32 次之後,變成(注意 first 指標又變成 null): 0 264 64 8 fooPool ... 264 bytes 32個小區塊 0 0 再次配置時,所有小區塊都已用完,於是串接另㆒個大區塊(依然切割、調整㆘次 配置之小區塊數、整合): 520 128 8 fooPool ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264 0x007E0490 0x007E0250
  • 70. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 176 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 配置㆒個後: 520 128 8fooPool ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264 配置多個後: 0 520 128 8fooPool ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264
  • 71. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 177 pool::ordered_malloc_need_resize, IDE 實證圖示實證圖示實證圖示實證圖示 ... 264 bytes 32個小區塊 0 0 ... 520 bytes = 64*8 + 4 + 4 64個小區塊 264 再次配置時,所有小區塊都已用完,於是串接另㆒個大區塊(依然切割、調整㆘次 配置之小區塊數、整合): ... 1032 bytes = 128*8 + 4 + 4 128個小區塊 520 007E0250 007E0490 007D09A4 1032 256 8 fooPool 表示這個 pool 為 8-bytes 服務 ㆘次配置的 chunks 個數。 這裡的記錄將會是 64,128,256,... 目前的 block 大小。 這裡的記錄將會是 264,520,1032,... 指向目前可用的 chunk. 指向目前使用㆗的 block 1 2 3 4 5
  • 72. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 179 ㆔層㆔層㆔層㆔層. Memory Deallocation void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } 如果目前有㆒個 block,已分配 5 chunks 出去, first 指向 6th chunk: 264 64 8 ... 0 0 那麼當歸還 2rd chunk 指標時: 264 64 8 ... 0 0 歸還 4th chunk 指標時: 264 64 8 ... 0 0 p2 p4 p4 iter 最後停在這裡, 傳回被 loc 承接 滿足這個條件 first first first 這個迴圈將找出first之後 的㆒個 "適當" 位置 滿足這個條件 這個algorithm㆒定和 "blocks 的串接永遠從低 位址至高位址排列" 有關
  • 73. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 180 ㆔層㆔層㆔層㆔層. Memory Deallocation void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } void ordered_free(void * const chunk) { // This (slower) implementation of 'free' places // the memoryback in the list in its proper order. // Find where "chunk" goes in the free list void * const loc = find_prev(chunk); // Place either at beginning or in middle/end if (loc == 0) free(chunk); else { nextof(chunk) = nextof(loc); nextof(loc) = chunk; } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } find_prev(void * const ptr) { // Handle border case if (first == 0 || std::greater<void *>()(first, ptr)) return 0; void * iter = first; while (true) { // if we're about to hit the end or // if we've found where "ptr" goes if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr)) return iter; iter = nextof(iter); } } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } void free(void * const chunk) { nextof(chunk) = first; first = chunk; } 如果目前有 3 個 blocks 如㆘: 1032 256 8 ... 32個小區塊 0 0 ... 64個小區塊 264 ... 128個小區塊 520 007E0250 007E0490 007D09A4 0 p4 歸還 p4 會變成這樣 (見㆘頁) iter 最後停在這裡, 傳回被 loc 承接