为什么将CString类型转换为wchar_t*会产生一个临时副本?如果我们使用其他类型而不是CString呢?

Why does casting a CString to wchar_t* yield a temporary copy? What if we used some other type instead of CString?

本文关键字:CString 如果 我们 副本 其他 一个 类型 wchar 类型转换 为什么      更新时间:2023-10-16

PVS studio发现了一个潜在的缺陷,我正在调查;警告V623 http://www.viva64.com/en/d/0240/

然而,没有足够的解释来理解为什么要做临时副本。如果CString::operator wchar_t*()定义如下:

wchar_t* newbuf = new wchar_t[20];
// do things to put the current CString value in there
return newbuf;

则存在内存泄漏,但没有指向已销毁的临时对象的指针。我看到只有一个其他可能的实现,这是返回一个已经存在的wchar_t*指针包含在CString中,这不会使一个新的临时。像这样:

return m_pContents;

两个问题;我在哪里可以找到CString的实际实现?而且,这个函数的什么有效实现(在任何自定义类型上)可以让转换操作符返回一个指向销毁的临时对象的指针?我只是不相信微软会这样实现它:

CString tempCopy(*this);
return tempCopy.m_pContents;

在您链接的pv工作室代码中,您有一个三元运算符取wchar_t *CString。您的问题不在于转换运算符,而是三元将wchar_t *提升为CString。您要么需要将结果赋值给新的CString,要么确保三元参数都是相同的类型,以便获得引用。

以下是CString::operator LPCTSTR的文档。LPCTSTRconst char*const wchar_t*的花哨的microsoft名称,取决于是否定义了_UNICODE

描述如下:

返回值

指向字符串数据的字符指针。

的评论

这个有用的强制转换操作符提供了一种有效的方法来访问CString对象中包含的以空结束的C字符串。没有字符被复制;只返回一个指针。小心这个操作员。如果在获得字符指针后更改CString对象,可能会导致内存重新分配,从而使指针失效。

那么,让我们继续你的问题。

我在哪里可以找到CString的实际实现?

您可以在microsoft c++运行库的源文件中找到它。除非你在微软工作,否则你可能无法访问。

我只是不相信微软会像这样实现它:

CString tempCopy(*this);
return tempCopy.m_pContents;

我也不相信。这将是复制,并且文档明确声明不进行复制。

现在是题目中的问题:

为什么将CString类型转换为wchar_t*会产生一个临时副本?

它没有。好吧,它确实创建了一个指针的副本,但它没有创建包含字符串的数组的副本。

你误解了这篇文章。下面是其中的破碎代码示例:
CString s1(L"1");
wchar_t s2[] = L"2";
bool a = false;
const wchar_t *s = a ? s1 : s2;

s2构造临时CString。在?:操作符结束后,该临时操作不存在。使用指针并没有错,因为它是由CString::operator wchar_t*()返回的。使用指针是错误的,因为CString::operator wchar_t*()是在临时对象上调用的。临时CString不是由CString::operator wchar_t*()创建的。它是由s2隐式创建的,因为s1的类型是CString

编辑。你在评论中问道:

你是怎么知道a的?s1: s2返回一个CString?

我知道无论布尔表达式是什么,条件运算符必须返回相同的类型。我知道,如果表达式返回不同的类型,并且一种类型可以隐式转换为另一种类型,那么操作符的返回类型将是另一种类型。我知道wchar_t*可以转换为CString。因此,条件运算符将返回一个CString

嗯,它可以返回CString,除了…CString也可以转换为wchar_t*,因此条件运算符可以返回wchar_t*CString,如果它确实返回CString,那么您最终会得到一个悬垂指针。你不能对它的回报做出假设,因为这两者都没有得到标准的保证。事实上,这使得程序形式不良,这是示例代码被破坏的另一种方式。以下是标准中的一段话:

[expr。§5.16/3(草案N3797)

…确定第二个操作数是否可以转换为匹配第三个操作数,以及第三个操作数是否可以转换为匹配第二个操作数。如果两者都可以转换,或者其中一个可以转换,但转换是不明确的,则程序是病态的。

"valid"(大双引号)的实现可以像

这样
class CString {
    wchar_t *data_;
    size_t max_size_;
    size_t size_;
    CString() : data_(new wchar_t[20]), max_data_(20), size_(0) {}
    //various operations on CString which might or might not do things like
    void extend(size_t new_size)
    {
        wchar_t *old_data = data_;
        data_ = new wchar_t[new_size];
        memcpy(data_, old_data_, size_);
        delete [] old_data;
        max_size_ = new_size;
    }
    //or even
    void append(wchar_t c)
    {
        data_[size_++] = c;
    }
    operator LPCSTR () const { data_[size_ + 1] = 0; return data_; }
};

因此,如果使用强制转换操作符,您将获得指向以空结束的字符串的指针。直到你(比如说)向字符串中添加一个字符为止。然后指针将指向一个非空结束的字符串。或者如果你向字符串中添加了很多字符,它可以释放内存并将其指向其他地方。在这种情况下,您的指针指向可能看起来有效的内容,直到下一次内存分配发生并获取最近释放的内存。

相关文章: