为啥使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时,原始 CString 会被覆盖?
Posted
技术标签:
【中文标题】为啥使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时,原始 CString 会被覆盖?【英文标题】:Why does the original CString get overwritten when passing a copy to the DrawText function with the DT_MODIFYSTRING option?为什么使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时,原始 CString 会被覆盖? 【发布时间】:2009-08-05 22:45:49 【问题描述】:我已经找到了解决此问题的方法,但只是想知道是否有人知道实际发生了什么导致我看到的问题。我的猜测是它与字符串的可变性有关,但我认为 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 在做什么?
【问题讨论】:
【参考方案1】:首先,请注意,CString 可以通过两种方式用作原始字符串指针:
operator LPCSTR
- 给出一个永远不应该修改的指针。
GetBuffer
- 提供一个指向内存的指针,专门用于修改字符串。
现在,DrawText 被声明为接受 LPCSTR。因此,当您像在代码中一样直接传递 CString 对象时,它会隐式使用 operator LPCSTR
来为函数提供它想要的东西,即一个常量字符串指针。
然而,DT_MODIFYSTRING 表示 DrawText 可以修改它给出的字符串。所以在内部,DrawText 肯定是丢掉了指针的 const 性,无论如何都要修改字符串。
这种组合是一件坏事。但问题主要在于DrawText的实现违反了自己的声明。
至于为什么这会修改其他 CString 对象:显然,当复制 CString 对象时,它会延迟复制内部字符串内存,直到有东西试图通过 CString 成员函数修改字符串。但在此之前,每个 CString 对象的operator LPCSTR
仍将指向相同的共享内部内存。这通常很好,只要任何使用它的代码都遵守 const 正确性规则。然而,正如我们已经看到的,带有 DT_MODIFYSTRING 的 DrawText 并没有按照规则进行。因此,它正在覆盖多个 CString 对象共享的内存。
因此,要解决此问题,如果您实际上不需要修改后的文本,则需要停止使用 DT_MODIFYSTRING。否则您需要使用filePath.GetBuffer()
将字符串传递给DrawText,然后再调用filePath.ReleaseBuffer()
。
【讨论】:
【参考方案2】:您发布的代码中存在一些差异:
在“类文件”中:
GetFileName() return mFileName;
这里没有返回类型?同样在 FileContainer 类中,您将存储的“文件”对象定义为指针,但在 GetFileName 函数中,您访问它时就好像它不是指针一样?
File* mFile;
...
mFile.GetFileName();
至于为什么现在情况很好,我真的说不出来。然而,另一种解决方法是将 GetFileName 函数更改为返回一个 const ref,这应该确保返回的值永远不会被更改。
const CString& GetFileName() return mFileName;
【讨论】:
以上是关于为啥使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时,原始 CString 会被覆盖?的主要内容,如果未能解决你的问题,请参考以下文章