c++ - 为什么在使用 DT_MODIFYSTRING 选项将拷贝传递给 DrawText 函数时原始 CString 会被覆盖?

标签 c++ mfc cstring

我已经找到解决此问题的方法,但只是想知道是否有人知道导致我所看到的问题的实际情况。我的猜测是它与字符串的可变性有关,但我认为 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 在做什么?

最佳答案

首先,请注意 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()

关于c++ - 为什么在使用 DT_MODIFYSTRING 选项将拷贝传递给 DrawText 函数时原始 CString 会被覆盖?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1236161/

相关文章:

c++ - 如何使大多数编辑器可折叠类实现的代码?

c++ - 在C++中分配为true或false的随机对象

c++ - MFC 资源/链接

windows-7 - 为什么快捷方式调用的命令在Windows 7下的MFC中永远不会崩溃

c++ - 尝试在 Visual C++ 中使用 CString 时出现语法错误

c++ - 从 COM 调用返回时接口(interface)未注册 (0x80040155)

c++ - 什么时候应该使用删除? (动态创建二维数组后不删除的后果)

visual-c++ - VS2008 : "Resource file opened in another editor"

c - 使用 strcmp 在数组中查找匹配项

C 字符串大小,包括空终止符