久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

VC知識庫文章

 mxhdrs 2005-11-25
它把 CString 強制類型轉(zhuǎn)化成 LPCTSTR,,也就是說先獲得改字符串的地址,然后再強制類型轉(zhuǎn)化成 LPTSTR,,以便可以對之進(jìn)行賦值操作,。 注意這只有在使用 Set 或 Insert 之類的方法才有效!如果你試圖獲取數(shù)據(jù),,則不能這么做,。

  如果你打算獲取存儲在控件中的數(shù)據(jù),則方法稍有不同,,例如,,對某個 CTreeCtrl 使用 GetItem 方法,,我想獲取項目的文本。我知道這些 文本的長度不會超過 MY_LIMIT,,因此我可以這樣寫:

TVITEM tvi;
// ... assorted initialization of other fields of tvi
tvi.pszText = s.GetBuffer(MY_LIMIT);
tvi.cchTextMax = MY_LIMIT;
c_MyTree.GetItem(&tvi);
s.ReleaseBuffer();

  可以看出來,,其實上面的代碼對所有類型的 Set 方法都適用,但是并不需要這么做,,因為所有的類 Set 方法(包括 Insert方法)不會改變字符串的內(nèi)容,。但是當(dāng)你需要寫 CString 對象時,必須保證緩沖是可寫的,,這正是 GetBuffer 所做的事情,。再次強調(diào): 一旦做了一次 GetBuffer 調(diào)用,那么在調(diào)用 ReleaseBuffer 之前不要對這個 CString 對象做任何操作,。

5,、CString 型轉(zhuǎn)化成 BSTR 型

  當(dāng)我們使用 ActiveX 控件編程時,經(jīng)常需要用到將某個值表示成 BSTR 類型,。BSTR 是一種記數(shù)字符串,,Intel平臺上的寬字符串(Unicode),并且 可以包含嵌入的 NULL 字符,。

你可以調(diào)用 CString 對象的 AllocSysString 方法將 CString 轉(zhuǎn)化成 BSTR:

CString s;
s = ... ; // whatever
BSTR b = s.AllocSysString();

  現(xiàn)在指針 b 指向的就是一個新分配的 BSTR 對象,,該對象是 CString 的一個拷貝,包含終結(jié) NULL字符?,F(xiàn)在你可以將它傳遞給任何需要 BSTR 的接口,。通常,BSTR 由接收它的組件來釋放,,如果你需要自己釋放 BSTR 的話,,可以這么做:

::SysFreeString(b);

  對于如何表示傳遞給 ActiveX 控件的字符串,在微軟內(nèi)部曾一度爭論不休,,最后 Visual Basic 的人占了上風(fēng),,BSTR(“Basic String”的首字母縮寫)就是這場爭論的結(jié)果。

6,、BSTR 型轉(zhuǎn)化成 CString 型

  由于 BSTR 是記數(shù) Unicode 字符串,,你可以用標(biāo)準(zhǔn)轉(zhuǎn)換方法來創(chuàng)建 8 位的 CString。實際上,,這是 CString 內(nèi)建的功能,。在 CString 中 有特殊的構(gòu)造函數(shù)可以把 ANSI 轉(zhuǎn)化成 Unicode,也可以把Unicode 轉(zhuǎn)化成 ANSI,。你同樣可以從 VARIANT 類型的變量中獲得 BSTR 類型的字符串,,VARIANT 類型是 由各種 COM 和 Automation (自動化)調(diào)用返回的類型。

例如,,在一個ANSI程序中:

BSTR b;
b = ...; // whatever
CString s(b == NULL ? L"" : b)

  對于單個的 BSTR 串來說,,這種用法可以工作得很好,,這是因為 CString 有一個特殊的構(gòu)造函數(shù)以LPCWSTR(BSTR正是這種類型) 為參數(shù),并將它轉(zhuǎn)化成 ANSI 類型,。專門檢查是必須的,,因為 BSTR 可能為空值,而 CString 的構(gòu)造函數(shù)對于 NULL 值情況考慮的不是很周到,,(感謝 Brian Ross 指出這一點!),。這種用法也只能處理包含 NUL 終結(jié)字符的單字符串;如果要轉(zhuǎn)化含有多個 NULL 字符 串,,你得額外做一些工作才行,。在 CString 中內(nèi)嵌的 NULL 字符通常表現(xiàn)不盡如人意,應(yīng)該盡量避免,。
  根據(jù) C/C++ 規(guī)則,,如果你有一個 LPWSTR,那么它別無選擇,,只能和 LPCWSTR 參數(shù)匹配,。

在 Unicode 模式下,它的構(gòu)造函數(shù)是:

CString::CString(LPCTSTR);

正如上面所表示的,,在 ANSI 模式下,,它有一個特殊的構(gòu)造函數(shù):

CString::CString(LPCWSTR); 

  它會調(diào)用一個內(nèi)部的函數(shù)將 Unicode 字符串轉(zhuǎn)換成 ANSI 字符串。(在Unicode模式下,,有一個專門的構(gòu)造函數(shù),,該函數(shù)有一個參數(shù)是LPCSTR類型——一個8位 ANSI 字符串 指針,該函數(shù)將它加寬為 Unicode 的字符串?。┰俅螐娬{(diào):一定要檢查 BSTR 的值是否為 NULL。
  另外還有一個問題,,正如上文提到的:BSTRs可以含有多個內(nèi)嵌的NULL字符,,但是 CString 的構(gòu)造函數(shù)只能處理某個串中單個 NULL 字符。 也就是說,,如果串中含有嵌入的 NUL字節(jié),,CString 將會計算出錯誤的串長度。你必須自己處理它,。如果你看看 strcore.cpp 中的構(gòu)造函數(shù),,你會發(fā)現(xiàn) 它們都調(diào)用了lstrlen,也就是計算字符串的長度,。
  注意從 Unicode 到 ANSI 的轉(zhuǎn)換使用帶專門參數(shù)的 ::WideCharToMultiByte,,如果你不想使用這種默認(rèn)的轉(zhuǎn)換方式,則必須編寫自己的轉(zhuǎn)化代碼,。
  如果你在 UNICODE 模式下編譯代碼,,你可以簡單地寫成:

CString convert(BSTR b)
{
    if(b == NULL)
        return CString(_T(""));
    CString s(b); // in UNICODE mode
    return s;
}
  如果是 ANSI 模式,,則需要更復(fù)雜的過程來轉(zhuǎn)換。注意這個代碼使用與 ::WideCharToMultiByte 相同的參數(shù)值,。所以你 只能在想要改變這些參數(shù)進(jìn)行轉(zhuǎn)換時使用該技術(shù),。例如,指定不同的默認(rèn)字符,,不同的標(biāo)志集等,。
CString convert(BSTR b)
{
    CString s;
    if(b == NULL)
       return s; // empty for NULL BSTR
#ifdef UNICODE
    s = b;
#else
    LPSTR p = s.GetBuffer(SysStringLen(b) + 1); 
    ::WideCharToMultiByte(CP_ACP,            // ANSI Code Page
                          0,                 // no flags
                          b,                 // source widechar string
                          -1,                // assume NUL-terminated
                          p,                 // target buffer
                          SysStringLen(b)+1, // target buffer length
                          NULL,              // use system default char
                          NULL);             // don‘‘t care if default used
    s.ReleaseBuffer();
#endif
    return s;
}
  我并不擔(dān)心如果 BSTR 包含沒有映射到 8 位字符集的 Unicode 字符時會發(fā)生什么,因為我指定了::WideCharToMultiByte 的最后兩個參數(shù)為 NULL,。這就是你可能需要改變的地方,。

7、VARIANT 型轉(zhuǎn)化成 CString 型

  事實上,,我從來沒有這么做過,,因為我沒有用 COM/OLE/ActiveX 編寫過程序。但是我在microsoft.public.vc.mfc 新聞組上看到了 Robert Quirk 的一篇帖子談到了這種轉(zhuǎn)化,,我覺得把他的文章包含在我的文章里是不太好的做法,,所以在這里多做一些解釋和演示。如果和他的文章有相孛的地方可能是我的疏忽,。
  VARIANT 類型經(jīng)常用來給 COM 對象傳遞參數(shù),,或者接收從 COM 對象返回的值。你也能自己編寫返回 VARIANT 類型的方法,,函數(shù)返回什么類型 依賴可能(并且常常)方法的輸入?yún)?shù)(比如,,在自動化操作中,依賴與你調(diào)用哪個方法,。IDispatch::Invoke 可能返回(通過其一個參數(shù))一個 包含有BYTE,、WORD、float,、double,、date、BSTR 等等 VARIANT 類型的結(jié)果,,(詳見 MSDN 上的 VARIANT 結(jié)構(gòu)的定義),。在下面的例子中,假設(shè) 類型是一個BSTR的變體,,也就是說在串中的值是通過 bsrtVal 來引用,,其優(yōu)點是在 ANSI 應(yīng)用中,有一個構(gòu)造函數(shù)會把 LPCWCHAR 引用的值轉(zhuǎn)換為一個 CString(見 BSTR-to-CString 部分),。在 Unicode 模式中,,將成為標(biāo)準(zhǔn)的 CString 構(gòu)造函數(shù),參見對缺省::WideCharToMultiByte 轉(zhuǎn)換的告誡,以及你覺得是否可以接受(大多數(shù)情況下,,你會滿意的),。
VARIANT vaData;
vaData = m_com.YourMethodHere();
ASSERT(vaData.vt == VT_BSTR);
CString strData(vaData.bstrVal);

你還可以根據(jù) vt 域的不同來建立更通用的轉(zhuǎn)換例程。為此你可能會考慮:

CString VariantToString(VARIANT * va)
{
    CString s;
    switch(va->vt)
      { /* vt */
       case VT_BSTR:
          return CString(vaData->bstrVal);
       case VT_BSTR | VT_BYREF:
          return CString(*vaData->pbstrVal);
       case VT_I4:
          s.Format(_T("%d"), va->lVal);
          return s;
       case VT_I4 | VT_BYREF:
          s.Format(_T("%d"), *va->plVal);
       case VT_R8:
          s.Format(_T("%f"), va->dblVal);
          return s;
       ... 剩下的類型轉(zhuǎn)換由讀者自己完成
       default:
          ASSERT(FALSE); // unknown VARIANT type (this ASSERT is optional)
          return CString("");
      } /* vt */
}
8,、載入字符串表資源

  如果你想創(chuàng)建一個容易進(jìn)行語言版本移植的應(yīng)用程序,,你就不能在你的源代碼中直接包含本土語言字符串 (下面這些例子我用的語言都是英語,因為我的本土語是英語),,比如下面這種寫法就很糟:
CString s = "There is an error";

  你應(yīng)該把你所有特定語言的字符串單獨擺放(調(diào)試信息,、在發(fā)布版本中不出現(xiàn)的信息除外)。這意味著向下面這樣寫比較好:

s.Format(_T("%d - %s"), code, text);

  在你的程序中,,文字字符串不是語言敏感的,。不管怎樣,你必須很小心,,不要使用下面這樣的串:

// fmt is "Error in %s file %s"
// readorwrite is "reading" or "writing"
s.Format(fmt, readorwrite, filename); 

  這是我的切身體會,。在我的第一個國際化的應(yīng)用程序中我犯了這個錯誤,盡管我懂德語,,知道在德語的語法中動詞放在句子的最后面,,我們的德國方面的發(fā)行人還是苦苦的抱怨他們不得不提取那些不可思議的德語錯誤提示信息然后重新格式化以讓它們能正常工作。比較好的辦法(也是我現(xiàn)在使用的辦法)是使用兩個字符串,,一個用 于讀,,一個用于寫,在使用時加載合適的版本,,使得它們對字符串參數(shù)是非敏感的,。也就是說加載整個格式,而不是加載串“reading”,,“writing”:

// fmt is "Error in reading file %s"
// "Error in writing file %s"
s.Format(fmt, filename);

  一定要注意,,如果你有好幾個地方需要替換,你一定要保證替換后句子的結(jié)構(gòu)不會出現(xiàn)問題,,比如在英語中,,可以是主語-賓語,主語-謂語,,動詞-賓語的結(jié)構(gòu)等等。
  在這里,,我們并不討論 FormatMessage,,其實它比 sprintf/Format 還要有優(yōu)勢,但是不太容易和CString 結(jié)合使用,。解決這種問題的辦法就是我們按照參數(shù)出現(xiàn)在參數(shù)表中的位置給參數(shù)取名字,,這樣在你輸出的時候就不會把他們的位置排錯了。
  接下來我們討論我們這些獨立的字符串放在什么地方。我們可以把字符串的值放入資源文件中的一個稱為 STRINGTABLE 的段中,。過程如下:首先使用 Visual Studio 的資源編輯器創(chuàng)建一個字符串,,然后給每一個字符串取一個ID,一般我們給它取名字都以 IDS_開頭,。所以如果你有一個信息,,你可以創(chuàng)建一個字符串資源然后取名為 IDS_READING_FILE,另外一個就取名為 IDS_WRITING_FILE,。它們以下面的形式出現(xiàn)在你的 .rc 文件中:

STRINGTABLE
IDS_READING_FILE "Reading file %s"
IDS_WRITING_FILE "Writing file %s"
END

注意:這些資源都以 Unicode 的格式保存,,不管你是在什么環(huán)境下編譯。他們在Win9x系統(tǒng)上也是以Unicode 的形式存在,,雖然 Win9x 不能真正處理 Unicode,。
然后你可以這樣使用這些資源:
// 在使用資源串表之前,程序是這樣寫的:

   CString fmt;
      if(...)
        fmt = "Reading file %s";
     else
       fmt = "Writing file %s";
  ...
    // much later
  CString s;
  s.Format(fmt, filename); 
// 使用資源串表之后,,程序這樣寫:
    CString fmt;
        if(...)
           fmt.LoadString(IDS_READING_FILE);
        else
           fmt.LoadString(DS_WRITING_FILE);
    ...
      // much later
    CString s;
    s.Format(fmt, filename);
  現(xiàn)在,,你的代碼可以移植到任何語言中去。LoadString 方法需要一個字符串資源的 ID 作為參數(shù),,然后它從 STRINGTABLE 中取出它對應(yīng)的字符串,,賦值給 CString 對象。 CString 對象的構(gòu)造函數(shù)還有一個更加聰明的特征可以簡化 STRINGTABLE 的使用,。這個用法在 CString::CString 的文檔中沒有指出,,但是在 構(gòu)造函數(shù)的示例程序中使用了。(為什么這個特性沒有成為正式文檔的一部分,,而是放在了一個例子中,,我記不得了!)——【譯者注:從這句話看,,作者可能是CString的設(shè)計者,。其實前面還有一句類似的話。說他沒有對使用GetBuffer(0)獲得的指針指向的地址是否可讀做有效性檢查 】,。這個特征就是:如果你將一個字符串資源的ID強制類型轉(zhuǎn)換為 LPCTSTR,,將會隱含調(diào)用 LoadString。因此,,下面兩個構(gòu)造字符串的例子具有相同的效果,,而且其 ASSERT 在debug模式下不會被觸發(fā):
CString s;
s.LoadString(IDS_WHATEVER);
CString t( (LPCTSTR)IDS_WHATEVER );
ASSERT(s == t);//不會被觸發(fā),說明s和t是相同的,。

  現(xiàn)在,,你可能會想:這怎么可能工作呢?我們怎么能把 STRINGTABLE ID 轉(zhuǎn)化成一個指針呢,?很簡單:所有的字符串 ID 都在1~65535這個范圍內(nèi),,也就是說,,它所有的高位都是0,而我們在程序中所使用的指針是不可能小于65535的,,因為程序的低 64K 內(nèi)存永遠(yuǎn)也不可能存在的,,如果你試圖訪問0x00000000到0x0000FFFF之間的內(nèi)存,將會引發(fā)一個內(nèi)存越界錯誤,。所以說1~65535的值不可能是一個內(nèi)存地址,,所以我們可以用這些值來作為字符串資源的ID。
  我傾向于使用 MAKEINTRESOURCE 宏顯式地做這種轉(zhuǎn)換,。我認(rèn)為這樣可以讓代碼更加易于閱讀,。這是個只適合在 MFC 中使用的標(biāo)準(zhǔn)宏。你要記住,,大多數(shù)的方法即可以接受一個 UINT 型的參數(shù),,也可以接受一個 LPCTSTR 型的參數(shù),這是依賴 C++ 的重載功能做到的,。C++重載函數(shù)帶來的 弊端就是造成所有的強制類型轉(zhuǎn)化都需要顯示聲明,。同樣,你也可以給很多種結(jié)構(gòu)只傳遞一個資源名,。

CString s;
s.LoadString(IDS_WHATEVER);
CString t( MAKEINTRESOURCE(IDS_WHATEVER));
ASSERT(s == t);

  告訴你吧:我不僅只是在這里鼓吹,,事實上我也是這么做的。在我的代碼中,,你幾乎不可能找到一個字符串,,當(dāng)然,那些只是偶然在調(diào)試中出現(xiàn)的或者和語言無關(guān)的字符串除外,。

9,、CString 和臨時對象

  這是出現(xiàn)在 microsoft.public.vc.mfc 新聞組中的一個小問題,我簡單的提一下,,這個問題是有個程序員需要往注冊表中寫入一個字符串,,他寫道:
  我試著用 RegSetValueEx() 設(shè)置一個注冊表鍵的值,但是它的結(jié)果總是令我困惑,。當(dāng)我用char[]聲明一個變量時它能正常工作,,但是當(dāng)我用 CString 的時候,總是得到一些垃圾:"ÝÝÝÝ...ÝÝÝÝÝÝ"為了確認(rèn)是不是我的 CString 數(shù)據(jù)出了問題,,我試著用 GetBuffer,,然后強制轉(zhuǎn)化成 char*,LPCSTR,。GetBuffer 返回的值是正確的,,但是當(dāng)我把它賦值給 char* 時,,它就變成垃圾了,。以下是我的程序段:

char* szName = GetName().GetBuffer(20);
RegSetValueEx(hKey, "Name", 0, REG_SZ, 
             (CONST BYTE *) szName,
             strlen (szName + 1));

這個 Name 字符串的長度小于 20,,所以我不認(rèn)為是 GetBuffer 的參數(shù)的問題。

真讓人困惑,,請幫幫我,。

親愛的 Frustrated,

你犯了一個相當(dāng)微妙的錯誤,,聰明反被聰明誤,,正確的代碼應(yīng)該象下面這樣:

CString Name = GetName();
RegSetValueEx(hKey, _T("Name"), 0, REG_SZ, 
                    (CONST BYTE *) (LPCTSTR)Name,
                    (Name.GetLength() + 1) * sizeof(TCHAR));
  為什么我寫的代碼能行而你寫的就有問題呢?主要是因為當(dāng)你調(diào)用 GetName 時返回的 CString 對象是一個臨時對象,。參見:《C++ Reference manual》§12.2
  在一些環(huán)境中,,編譯器有必要創(chuàng)建一個臨時對象,這樣引入臨時對象是依賴于實現(xiàn)的,。如果編譯器引入的這個臨時對象所屬的類有構(gòu)造函數(shù)的話,,編譯器要確保這個類的構(gòu)造函數(shù)被調(diào)用。同樣的,,如果這個類聲明有析構(gòu)函數(shù)的話,,也要保證這個臨時對象的析構(gòu)函數(shù)被調(diào)用。
  編譯器必須保證這個臨時對象被銷毀了,。被銷毀的確切地點依賴于實現(xiàn).....這個析構(gòu)函數(shù)必須在退出創(chuàng)建該臨時對象的范圍之前被調(diào)用,。
  大部分的編譯器是這樣設(shè)計的:在臨時對象被創(chuàng)建的代碼的下一個執(zhí)行步驟處隱含調(diào)用這個臨時對象的析構(gòu)函數(shù),實現(xiàn)起來,,一般都是在下一個分號處,。因此,這個 CString 對象在 GetBuffer 調(diào)用之后就被析構(gòu)了(順便提一句,,你沒有理由給 GetBuffer 函數(shù)傳遞一個參數(shù),,而且沒有使用ReleaseBuffer 也是不對的)。所以 GetBuffer 本來返回的是指向這個臨時對象中字符串的地址的指針,,但是當(dāng)這個臨時對象被析構(gòu)后,,這塊內(nèi)存就被釋放了。然后 MFC 的調(diào)試內(nèi)存分配器會重新為這塊內(nèi)存全部填上 0xDD,,顯示出來剛好就是“Ý”符號,。在這個時候你向注冊表中寫數(shù)據(jù),字符串的內(nèi)容當(dāng)然全被破壞了,。
  我們不應(yīng)該立即把這個臨時對象轉(zhuǎn)化成 char* 類型,,應(yīng)該先把它保存到一個 CString 對象中,這意味著把臨時對象復(fù)制了一份,,所以當(dāng)臨時的 CString 對象被析構(gòu)了之后,,這個 CString 對象中的值依然保存著。這個時候再向注冊表中寫數(shù)據(jù)就沒有問題了,。
  此外,,我的代碼是具有 Unicode 意識的,。那個操作注冊表的函數(shù)需要一個字節(jié)大小,使用lstrlen(Name+1) 得到的實際結(jié)果對于 Unicode 字符來說比 ANSI 字符要小一半,,而且它也不能從這個字符串的第二個字符起開始計算,,也許你的本意是 lstrlen(Name) + 1(OK,我承認(rèn),,我也犯了同樣的錯誤?。2徽撊绾?,在 Unicode 模式下,,所有的字符都是2個字節(jié)大小,我們需要處理這個問題,。微軟的文檔令人驚訝地對此保持緘默:REG_SZ 的值究竟是以字節(jié)計算還是以字符計算呢,?我們假設(shè)它指的是以字節(jié)為單位計算,你需要對你的代碼做一些修改來計算這個字符串所含有的字節(jié)大小,。

10,、CString 的效率

  CString 的一個問題是它確實掩藏了一些低效率的東西。從另外一個方面講,,它也確實可以被實現(xiàn)得更加高效,,你可能會說下面的代碼:
CString s = SomeCString1;
s += SomeCString2;
s += SomeCString3;
s += ",";
s += SomeCString4;

比起下面的代碼來,效率要低多了:

char s[1024];
lstrcpy(s, SomeString1);
lstrcat(s, SomeString2);
lstrcat(s, SomeString 3);
lstrcat(s, ",");
lstrcat(s, SomeString4);

  總之,,你可能會想,,首先,它為 SomeCString1 分配一塊內(nèi)存,,然后把 SomeCString1 復(fù)制到里面,,然后發(fā)現(xiàn)它要做一個連接,則重新分配一塊新的足夠大的內(nèi)存,,大到能夠放下當(dāng)前的字符串加上SomeCString2,,把內(nèi)容復(fù)制到這塊內(nèi)存 ,然后把 SomeCString2 連接到后面,,然后釋放第一塊內(nèi)存,,并把指針重新指向新內(nèi)存。然后為每個字符串重復(fù)這個過程,。把這 4 個字符串連接起來效率多低啊,。事實上,在很多情況下根本就不需要復(fù)制源字符串(在 += 操作符左邊的字符串),。
  在 VC++6.0 中,,Release 模式下,所有的 CString 中的緩存都是按預(yù)定義量子分配的,。所謂量子,,即確定為 64,、128、256 或者 512 字節(jié),。這意味著除非字符串非常長,,連接字符串的操作實際上就是 strcat 經(jīng)過優(yōu)化后的版本(因為它知道本地的字符串應(yīng)該在什么地方結(jié)束,所以不需要尋找字符串的結(jié)尾,;只需要把內(nèi)存中的數(shù)據(jù)拷貝到指定的地方即可)加上重新計算字符串的長度。所以它的執(zhí)行效率和純 C 的代碼是一樣的,,但是它更容易寫,、更容易維護(hù)和更容易理解。
  如果你還是不能確定究竟發(fā)生了怎樣的過程,,請看看 CString 的源代碼,,strcore.cpp,在你 vc98的安裝目錄的 mfc\src 子目錄中,??纯?ConcatInPlace 方法,它被在所有的 += 操作符中調(diào)用,。

啊哈,!難道 CString 真的這么"高效"嗎?比如,,如果我創(chuàng)建

CString cat("Mew!");

  然后我并不是得到了一個高效的,、精簡的5個字節(jié)大小的緩沖區(qū)(4個字符加一個結(jié)束字符),系統(tǒng)將給我分配64個字節(jié),,而其中59個字節(jié)都被浪費了,。
  如果你也是這么想的話,那么就請準(zhǔn)備好接受再教育吧,??赡茉谀硞€地方某個人給你講過盡量使用少的空間是件好事情。不錯,,這種說法的確正確,,但是他忽略了事實中一個很重要的方面。
  如果你編寫的是運行在16K EPROMs下的嵌入式程序的話,,你有理由盡量少使用空間,,在這種環(huán)境下,它能使你的程序更健壯,。但是在 500MHz, 256MB的機器上寫 Windows 程序,,如果你還是這么做,它只會比你認(rèn)為的“低效”的代碼運行得更糟,。
  舉例來說,。字符串的大小被認(rèn)為是影響效率的首要因素,,使字符串盡可能小可以提高效率,反之則降低效率,,這是大家一貫的想法,。但是這種想法是不對的,精確的內(nèi)存分配的后果要在程序運行了好幾個小時后才能體現(xiàn)得出來,,那時,,程序的堆中將充滿小片的內(nèi)存,它們太小以至于不能用來做任何事,,但是他們增加了你程序的內(nèi)存用量,,增加了內(nèi)存頁面交換的次數(shù),當(dāng)頁面交換的次數(shù)增加到系統(tǒng)能夠忍受的上限,,系統(tǒng)則會為你的程序分配更多的頁面,,直到你的程序占用了所有的可用內(nèi)存。由此可見,,雖然內(nèi)存碎片是決定效率的次要因素,,但正是這些因素實際控制了系統(tǒng)的行為,最終,,它損害了系統(tǒng)的可靠性,,這是令人無法接受的。
  記住,,在 debug 模式下,,內(nèi)存往往是精確分配的,這是為了更好的排錯,。
  假設(shè)你的應(yīng)用程序通常需要連續(xù)工作好幾個月,。比如,我常打開 VC++,,Word,,PowerPoint,F(xiàn)rontpage,,Outlook Express,,F(xiàn)orté Agent,Internet Explorer和其它的一些程序,,而且通常不關(guān)閉它們,。我曾經(jīng)夜以繼日地連續(xù)用 PowerPoint 工作了好幾天(反之,如果你不幸不得不使用像 Adobe FrameMaker 這樣的程序的話,,你將會體會到可靠性的重要,;這個程序機會每天都要崩潰4~6次,每次都是因為用完了所有的空間并填滿我所有的交換頁面)。所以精確內(nèi)存分配是不可取的,,它會危及到系統(tǒng)的可靠性,,并引起應(yīng)用程序崩潰。
  按量子的倍數(shù)為字符串分配內(nèi)存,,內(nèi)存分配器就可以回收用過的內(nèi)存塊,,通常這些回收的內(nèi)存塊馬上就可以被其它的 CString 對象重新用到,這樣就可以保證碎片最少,。分配器的功能加強了,,應(yīng)用程序用到的內(nèi)存就能盡可能保持最小,這樣的程序就可以運行幾個星期或幾個月而不出現(xiàn)問題,。
  題外話:很多年以前,,我們在 CMU 寫一個交互式系統(tǒng)的時候,一些對內(nèi)存分配器的研究顯示出它往往產(chǎn)生很多內(nèi)存碎片,。Jim Mitchell,現(xiàn)在他在 Sun Microsystems 工作,,那時侯他創(chuàng)造了一種內(nèi)存分配器,,它保留了一個內(nèi)存分配狀況的運行時統(tǒng)計表,這種技術(shù)和當(dāng)時的主流分配器所用的技術(shù)都不同,,且較為領(lǐng)先,。當(dāng)一個內(nèi)存塊需要被分割得比某一個值小的話,他并不分割它,,因此可以避免產(chǎn)生太多小到什么事都干不了的內(nèi)存碎片,。事實上他在內(nèi)存分配器中使用了一個浮動指針,他認(rèn)為:與其讓指令做長時間的存取內(nèi)存操作,,還不如簡單的忽略那些太小的內(nèi)存塊而只做一些浮動指針的操作,。(His observation was that the long-term saving in instructions by not having to ignore unusable small storage chunks far and away exceeded the additional cost of doing a few floating point operations on an allocation operation.)他是對的。
  永遠(yuǎn)不要認(rèn)為所謂的“最優(yōu)化”是建立在每一行代碼都高速且節(jié)省內(nèi)存的基礎(chǔ)上的,,事實上,,高速且節(jié)省內(nèi)存應(yīng)該是在一個應(yīng)用程序的整體水平上考慮的。在軟件的整體水平上,,只使用最小內(nèi)存的字符串分配策略可能是最糟糕的一種方法,。
  如果你認(rèn)為優(yōu)化是你在每一行代碼上做的那些努力的話,你應(yīng)該想一想:在每一行代碼中做的優(yōu)化很少能真正起作用,。你可以看我的另一篇關(guān)于優(yōu)化問題的文章《Your Worst Enemy for some thought-provoking ideas》,。
  記住,+= 運算符只是一種特例,,如果你寫成下面這樣:

CString s = SomeCString1 + SomeCString2 + SomeCString3 + "," + SomeCString4;

則每一個 + 的應(yīng)用會造成一個新的字符串被創(chuàng)建和一次復(fù)制操作,。

總結(jié)

  以上是使用 CString 的一些技巧。我每天寫程序的時候都會用到這些,。CString 并不是一種很難使用的類,,但是 MFC 沒有很明顯的指出這些特征,,需要你自己去探索、去發(fā)現(xiàn),。




最新評論 [發(fā)表評論] [文章投稿] 查看所有評論 推薦給好友 打印

翻譯的很好 以后再接再厲 多多翻譯好作品,。 ( flyingleaf 發(fā)表于 2004-4-30 13:23:00)
 
excellent ( Jerry_知識庫 發(fā)表于 2004-4-26 9:32:00)
 
非常精彩! ( 遠(yuǎn)帆 發(fā)表于 2004-4-15 13:11:00)
 
www.codeproject.com上有很多類似的文章,。 ( xiaojin 發(fā)表于 2004-4-15 12:24:00)
 
此類總結(jié)性文章非常好,!
謝謝! ( ligang 發(fā)表于 2004-4-15 9:22:00)
 
歡迎加盟中國微型軟件開發(fā)中心,,網(wǎng)站正建設(shè)中,。。,。
為你量身定做各種小型軟件,,
http://minisoft./
聯(lián)系方式:
email:[email protected]
qq   : 36201365 ( rbird5118 發(fā)表于 2004-4-14 15:54:00)
 
.......................................................
More...


版權(quán)所有 ? 2004 VC知識庫 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點,。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多