2009-08-05 95 views
1

我已經找到了解決這個問題的方法,但只是想知道是否有人知道實際發生了什麼事情會導致我看到的問題。我的猜測是它與字符串的可變性有關,但我認爲CString對象在拷貝構造函數中佔了這一地位。爲什麼使用DT_MODIFYSTRING選項將副本傳遞給DrawText函數時,原始CString會被覆蓋?

下面的代碼將導致mFileName被覆蓋:

class File { 
public: 
... 
CString GetFilename() {return mFileName;} 
private: 
CString mFileName; 
}; 

class FileContainer { 
private: File* mFile; 
public: 
FileContainer() { 
    mFile = new File("C:\temp.txt"); 
} 
GetFilename(CString& fileName) { 
    fileName = mFile->GetFileName(); 
} 
} 

void UpdateText() { 
FileContainer fileCnt; 
CString filePath(L""); 
this->fileCnt.GetFilename(filePath); 
... 
::DrawText(hDC, filePath, -1, &destRect, DT_PATH_ELLIPSIS | DT_MODIFYSTRING | DT_CALCRECT); 
} 

什麼情況是,第一次UPDATETEXT被調用時,用GetFileName返回C:\ TEMP.TXT。假設邊界矩形導致文本在第一次調用時被截斷爲「... \ temp.txt」,則「... \ temp.txt」是在第二次調用UpdateText時從GetFilename返回的內容。

更令人費解的是,這並沒有引起mFileName改變:

void UpdateText() { 
FileContainer fileCnt; 
CString filePath(L""); 
this->fileCnt->GetFilename(filePath); 
filePath = L"TEST"; 
} 

用GetFileName總是返回C:\ TEMP.TXT。所以看起來,DrawText函數是以某種方式找到原始的CString並修改它。但是如何?

更新:我想我會扔另一個代碼塊奇也導致mFileName被覆蓋:

class File { 
public: 
... 
CString GetFilename() {return CString(mFileName);} 
private: 
CString mFileName; 
}; 

這似乎像它應該創建一個新的對象,並返回該新對象。然而,不知何故,DrawText仍然會覆蓋mFileName。

如果我改變了代碼以下,我沒有任何問題:

class File { 
public: 
... 
CString GetFilename() {return CString(mFileName.GetBuffer());} 
private: 
CString mFileName; 
}; 

,似乎解決問題的唯一的事情是建立一個新的CString我的解決方法顯示的方式。當我通過DT_MODIFYSTRING選項時DrawText在做什麼?

回答

4

首先,需要注意的是CString的,可以以兩種方式作爲原始字符串指針:

  1. operator LPCSTR - 給出了一個不應該被修改的指針。
  2. GetBuffer - 給出一個指向內存的指針,專門用於修改字符串。

現在,DrawText被聲明爲接受LPCSTR。所以當你直接傳遞一個CString對象的時候,它隱含地使用operator LPCSTR來給函數說明它想要的東西,一個常量字符串指針。

但是,DT_MODIFYSTRING說DrawText可以修改它給出的字符串。所以在內部,DrawText必須拋棄指針的常量並修改字符串。

這種組合是一件壞事。但是錯誤主要在於DrawText的違反它自己的聲明。

至於爲什麼這會修改其他CString對象:顯然,當CString對象被複制時,它會延遲複製內部字符串內存,直到試圖通過CString成員函數修改字符串。但在此之前,每個CString對象的operator LPCSTR仍然會指向相同的共享內存。這通常很好,只要任何使用它的代碼遵守const-correctness的規則。但是,正如我們已經看到的,帶DT_MODIFYSTRING的DrawText不是按規則玩的。因此,它覆蓋了多個CString對象共享的內存。

因此,要解決此問題,如果您實際上不需要修改的文本,則需要停止使用DT_MODIFYSTRING。否則,您需要使用filePath.GetBuffer()將字符串傳遞給DrawText,然後再調用filePath.ReleaseBuffer()

0

那麼有代碼中存在一些差異,您張貼:

在「類文件」:

GetFileName() {return mFileName;} 

沒有返回類型嗎?同樣在FileContainer類中,您將存儲的「File」對象定義爲指針,但在GetFileName函數中,您可以像訪問它不是指針那樣訪問它?

File* mFile; 
... 
mFile.GetFileName(); 

至於爲什麼現在發生這種情況我不知道。另一個解決方法是改變GetFileName函數返回一個const ref,這應該確保返回的值永遠不會被改變。

const CString& GetFileName() { return mFileName; } 
相關問題