1

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

2 回答 2

4

首先,请注意 CString 可以通过两种方式用作原始字符串指针:

  1. operator LPCSTR- 给出一个永远不应该修改的指针。
  2. GetBuffer- 提供一个指向内存的指针,专门用于修改字符串。

现在,DrawText 被声明为接受 LPCSTR。因此,当您像在代码中一样直接传递 CString 对象时,它会隐式地使用operator LPCSTR常量字符串指针来为函数提供它所说的内容。

但是,DT_MODIFYSTRING 表示 DrawText 可以修改它给出的字符串。所以在内部,DrawText 必须丢弃指针的常量并修改字符串。

这种组合是一件坏事。但问题主要在于 DrawText 的实现违反了自己的声明。

至于为什么这会修改其他 CString 对象:显然,当复制 CString 对象时,它会延迟复制内部字符串内存,直到有东西试图通过 CString 成员函数修改字符串。但在此之前,operator LPCSTR每个 CString 对象的 仍将指向相同的共享内部内存。这通常很好,只要任何使用它的代码都遵守 const 正确性规则。然而,正如我们已经看到的,带有 DT_MODIFYSTRING 的 DrawText 并没有按照规则进行。因此,它覆盖了多个 CString 对象共享的内存。

因此,要解决此问题,如果您实际上不需要修改后的文本,则需要停止使用 DT_MODIFYSTRING。否则,您需要使用将字符串传递给 DrawText filePath.GetBuffer(),然后再调用filePath.ReleaseBuffer()

于 2009-08-05T23:25:34.640 回答
0

那么您发布的代码中存在一些差异:

在“类文件”中:

GetFileName() {return mFileName;}

这里没有返回类型?同样在 FileContainer 类中,您将存储的“文件”对象定义为指针,但在 GetFileName 函数中,您访问它就好像它不是指针一样?

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

至于为什么现在这件事发生得很好,我真的说不出来。然而,另一种解决方法是将 GetFileName 函数更改为返回一个 const ref,这应该确保返回的值永远不会被更改。

const CString& GetFileName() { return mFileName; }
于 2009-08-05T23:09:50.150 回答