在使用MFC開發時,字符串類CString大概是使用最多的類之一,它使得對字符串的處理變得很方便。但是,在一些場合下,CString存在一些明顯的效率問題。
如下面的代碼
CString b = "Hello world!";
INT n = b.GetLength();
for( INT i = 0; i < n; i++ )
{
a += b[i];
a += TEXT("\r\n");
}
這段代碼將字符串b中的每個字符拼接到字符串a,並在後面插入0x0D 0x0A。
當b為"Hello, world!"這樣簡單的字串時,程序的執行起來尚無問題。但是若b的字串很長的時候,就會出現明顯的效率問題。我使用自己的計算機做了一系列測試,得出如下的結果
字符串b的大小 執行時間
-------------─
001k 0.01s
010k 0.41s
020k 1.68s
040k 6.02s
080k 33.56s
100k 80.46s
可以說,當b的大小超過20K時,已經會出現明顯的延遲,當超過40k,用戶就會難以忍受了。
為甚麼會出現這樣的情況?我們會想到,當被拼接的字符串b越長,要複製和插入到字串a的字就越多,循環的次數就越多,導致時間的增加。但是這只是一個方面,更為重要的原因在於--
CString的拼接操作符"+"看似簡單,卻要完成相當多的操作。首先會對CString對象(例子中字串a)的數據緩衝區進行重新分配,接著找到字串的結束位置,將要拼接的串複製到這個位置開始的緩衝區中。隨著拼接的進行,CString對象的字串隨之變長,這些操作所需要的時間開銷也會逐步增加。
如何解決這個問題呢,可以考慮,如果字串a的數據緩衝區一開始就固定下來,那麼此後在每次拼接前就不必去重新分配了;同時,用一個指針來指向已有字串的結束位置,就不必去尋找這個位置了。因此,將程序改寫如下。
PTCHAR buf, p;
CString b = "Hello, world!";
INT n = b.GetLength();
PTCHAR q = b.GetBuffer(n);
p = buf = (PTCHAR)malloc( sizeof(TCHAR) * (n*3+1) );
while( *q )
{
*p++ = *q++;
*p++ = '\r';
*p++ = '\n';
}
*p = '\0';
a = buf;
free( buf );
再次測試後的結果是
字符串b的大小 執行時間
-------------─
040k 0.01s
100k 0.01s
500k 0.06s
效率的提昇是多麼明顯。
題外話,這個例子的一個啟示是,當遇到程序執行出現延遲的情形,可以首先從程序本身找原因,想辦法替換掉那些效率不高的元操作。如果確認已經優化過了,還可以考慮其他的方法如多線程,來完成任務。