为什么使用 DT_MODIFYSTRING 选项将副本传递给 DrawText 函数时原始 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 在做什么?
I've already found a workaround to this problem, but was just wondering if anyone knew what was actually happening to cause the problem I was seeing. My guess is that it has something to do with mutability of strings, but I thought the CString object accounted for that in the copy constructor.
The following code causes mFileName to be overwritten:
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);
}
What happens is that the first time UpdateText is called, GetFilename returns C:\temp.txt. Assuming that the bounding rect caused the text to be truncated to "...\temp.txt" on the first call, "...\temp.txt" is what is returned from GetFilename on the second call to UpdateText.
Even more perplexing is that this didn't cause mFileName to be changed:
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt->GetFilename(filePath);
filePath = L"TEST";
}
GetFilename always returned C:\temp.txt. So it would seem that the DrawText function is somehow finding the original CString and modifying it. But how?
UPDATE: I figured I'd throw another odd chunk of code that also causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return CString(mFileName);}
private:
CString mFileName;
};
That seems like it should create a new object and return that new object. Yet, somehow, DrawText still overwrites mFileName.
If I change the code to the following, I don't have any issues:
class File {
public:
...
CString GetFilename() {return CString(mFileName.GetBuffer());}
private:
CString mFileName;
};
The only thing that seems to solve the problem is to construct a new CString the way I showed in the workaround. What is DrawText doing when I pass the DT_MODIFYSTRING option?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
那么您发布的代码中存在一些差异:
在“类文件”中:
这里没有返回类型? 同样在 FileContainer 类中,您将存储的“File”对象定义为指针,但在 GetFileName 函数中,您访问它就像它不是指针一样?
至于为什么现在这种情况发生得很好,我真的说不出来。 然而,另一种解决方法是更改 GetFileName 函数以返回 const 引用,这应该确保返回的值永远不会更改。
Well there are some discrepancies in the code that you posted:
In 'class File':
There is no return type here? Also in the FileContainer class you define the stored 'File' object as a pointer, but in the GetFileName function you access it as if it was not a pointer?
As far as why this is happening well right now I can't really tell. Another work around however would be to change the GetFileName function to return a const ref, this should ensure that the returned value can never be changed.
首先,请注意,CString 可以通过两种方式用作原始字符串指针:
GetBuffer
- 给出一个指向内存的指针,专门用于修改字符串。现在,DrawText 被声明接受 LPCSTR。 因此,当您像在代码中一样直接传递 CString 对象时,它会隐式使用
运算符 LPCSTR
为函数提供它所说的内容,即常量字符串指针。然而,DT_MODIFYSTRING 表示 DrawText 可以修改给定的字符串。 因此,在内部,DrawText 必须丢弃指针的常量性并无论如何修改字符串。
这种组合是一件坏事。 但错误主要在于 DrawText 的实现违反了它自己的声明。
至于为什么这会修改其他CString对象:显然,当复制CString对象时,它会延迟复制内部字符串内存,直到有东西尝试通过CString成员函数修改字符串。 但在此之前,每个 CString 对象的运算符 LPCSTR 仍将指向相同的共享内部内存。 这通常没问题,只要使用它的任何代码都遵守常量正确性规则。 然而,正如我们已经看到的,使用 DT_MODIFYSTRING 的 DrawText 并不按规则进行。 因此,它会覆盖多个 CString 对象共享的内存。
因此,要解决此问题,如果您实际上不需要修改的文本,则需要停止使用 DT_MODIFYSTRING。 否则,您需要使用 filePath.GetBuffer() 将字符串传递给 DrawText,然后调用 filePath.ReleaseBuffer()。
First, note that CString can be used as a raw string pointer in two ways:
operator LPCSTR
- gives a pointer which should never be modified.GetBuffer
- gives a pointer to memory specifically for the purpose of modifying the string.Now, DrawText is declared to accept a LPCSTR. So when you pass a CString object directly as in your code, it implicitly uses
operator LPCSTR
to give the function what it says it wants, a constant string pointer.However, DT_MODIFYSTRING says that DrawText can modify the string it was given. So internally, DrawText must be throwing away the constness of the pointer and modifying the string anyway.
This combination is a bad thing. But the fault is mainly in the implmentation of DrawText which is violating its own declaration.
As for why this modifies other CString objects: Apparently when a CString object is copied, it delays copying the internal string memory until something tries to modify the string through a CString member function. But until that happens, the
operator LPCSTR
of each CString object would still point to the same shared internal memory. This is normally fine, as long as any code using it is obeying the rules of const-correctness. However, as we've already seen, DrawText with DT_MODIFYSTRING is not playing by the rules. Thus, it is overwriting memory shared by multiple CString objects.So to fix this problem, you either need to stop using DT_MODIFYSTRING if you don't actually need the modified text. Or else you need to pass the string to DrawText using
filePath.GetBuffer()
and then callfilePath.ReleaseBuffer()
afterwards.