C 字串與享元(Flyweight)模式

2021-09-09 02:57:59 字數 2418 閱讀 2691

寫這個文章,主要是因為網上對c#字串和享元模式的誤解比較多。

先說這名字,fly呢,就是蒼蠅,沒錯這裡面不是飛的意思,是蒼蠅的意思,weight大家都知道,就是重量,蒼蠅的重量,就是非常非常輕的意思。所以flyweight模式就是處理非常非常輕量級物件的乙個東西。

flyweight的目標是解決大量細粒度物件的記憶體消耗問題,當然,巧婦難為無公尺之炊,任何模式和手法都不能憑空造出記憶體來,所以享元模式針對的情況是這些細粒度物件的中資料有重複的情況。

flyweight的做法是,把物件的狀態(通常用屬性表示),分成兩個部分,一部分是內部狀態,另一部分是外部狀態。

內部狀態

外部狀態是不易重複的(或者說必要的),

外部狀態

內部狀態是易重複的。所以,flyweight把外部狀態提取出來共享,這樣就一定程度解決了記憶體占用問題。

在網上常常可以看到乙個說法,說c#中的字串使用了flyweight模式,開門見山地說,這個說法是錯誤的。

錯在**呢?按照上文的介紹,錯就錯在字串它沒有所謂的「

內部狀態

外部狀態」。

通常講字串是享元的原因就是以下**:

string a = "hello world";

console.writeline(object.referenceequals(a, "hello world")); //true

當使用字串直接量的時候,不論你寫了多少個"hello world",最終記憶體裡面只有乙個字串物件。

執行時建立的字串並不在此列,可以使些手段,強制在記憶體裡面產生新的字串。

string a = "hello world";

console.writeline(object.referenceequals(a, new string("hello world".tochararray())));  //false

因為我們強行呼叫了new,所以這個字串跟記憶體中的直接量"hello world"對應的物件不是同乙個。

有趣的是,c#還允許強制把乙個字串加入到(如果已經有了,就只是找出來)字串池裡面。

string a = "hello world";

string b = string.intern(new string("hello world".tochararray()));

console.writeline(object.referenceequals(a,b) );   

或者string a = string.intern(new string("hello world".tochararray()));

string b = string.intern(new string("hello world".tochararray()));

console.writeline(object.referenceequals(a,b) );

前面提到了,這個行為跟flyweight使用的內部狀態和外部狀態不同,是兩個物件實實在在就是同乙個物件。

好吧,前面說了不少,c#中的字串不是flyweight模式,但是是不是就意味著c#裡面字串跟flyweight沒有關係呢?

當然不是,否則我寫這麼一篇文章豈不是太蛋疼了……

字串池和intern方法簡直是實現flyweight的神器啊!

考慮我們有某一類物件,可能會建立幾百萬個,物件裡面恰巧有這麼乙個屬性叫做顏色,它在物件構造的時候隨機產生,顏色用的是rgb色,用rgb24來表示,於是顏色字串類似#ccc這樣子。

**寫起來就像下面的樣子:

class element

public string color;

public element()

}

接下來我們建立3千萬個物件看看如何

element eles = new element[30000000];

for (var i = 0; i < 30000000; i++)

從任務管理器看到一大塊記憶體被吃掉了

接下來我們使用string.intern來實現flyweight:

class element

public string color;

public element()

}

同樣來看看執行結果:

可以看到記憶體佔用量的明顯變化。

因為字串物件的不可更改性質,使用了string.intern之後,我們完全看不出前後color的區別,也就是說,修改前後的element類是完全等效的,但是flyweight為我們節約了大量的記憶體。

這個典型的使用flyweight場景為我們揭示了享元

外部狀態

內部狀態的特徵:像字串一樣不可更改的物件。gof原書的例子中的字型物件glyph也是如此。

string.intern這種物件池的方式實現flyweight也值得借鑑,我們可以考慮自己設計flyweight的外部狀態物件時使用類似的方式。

C風格字串與C 風格字串

c風格字串 對字串進行操作的 c 函式定義在標頭檔案中 1.字串定義 char result 2.字串的最後乙個字元是null字元 0 可以通過這個字元確定字串的結尾。3.strlen 返回的是字串的大小 因此,分配空間的時候,需要比字串的實際空間大1.e.g.char copystring con...

C風格字串與C 風格字串

c風格字串 對字串進行操作的 c 函式定義在標頭檔案中 1.字串定義 char result 2.字串的最後乙個字元是null字元 0 可以通過這個字元確定字串的結尾。3.strlen 返回的是字串的大小 因此,分配空間的時候,需要比字串的實際空間大1.e.g.char copystring con...

C 字串指標與字串陣列

在做面試100題中第21題時,發現char astr abcdefghijk 0 和char astr 有點區別,以前一直以為是一樣的,但是在該程式中採用字串指標執行一直出錯。後來在網上查查,果然發現大大的不同。分析 當你需要修改字串時,採用指標指向該字串編譯通過但是執行出錯,而採用字串陣列時不會出...