没有大小参数的CString::GetBuffer()做什么

What does CString::GetBuffer() with no size parameter do?

本文关键字:GetBuffer 什么 CString 参数      更新时间:2023-10-16

也许我快疯了,但我已经尝试了我能想到的每一个搜索组合,但我找不到没有参数的CString::GetBuffer()的定义。我查找的每个引用都描述了CString::GetBuffer( int ),其中传入的int参数是最大缓冲区长度。标头中的定义适用于CSimpleStringT::GetBuffer()。这给了我以下链接,它至少承认了无参数版本的存在,但没有提供对其行为的描述。https://msdn.microsoft.com/en-us/library/sddk80xf.aspx#csimplestringt__getbuffer

我正在研究现有的C++(Visual Studio)代码,如果不需要的话,我不想更改这些代码,但我需要知道CString::GetBuffer()的预期行为。如果有人能解释一下或给我指一些文件,我将不胜感激。

尽管msdn文档并没有真正说明没有参数的GetBuffer的作用,但MFC源代码揭示了答案:

return( m_pszData );

所以它只返回一个指向底层字符缓冲区的指针。(它还检查内部数据是否共享,并首先分叉/复制它)。

代码在atlsimpstr.h 中

完整功能:

PXSTR GetBuffer()
{
CStringData* pData = GetData();
if( pData->IsShared() )
{
Fork( pData->nDataLength );
}
return( m_pszData );
}

tl;dr

致电CString::GetString()


这是出于错误的原因提出错误的问题。为了解决这个问题,下面是文档中的答案:

返回值
指向对象(以null结尾)字符缓冲区的PXSTR指针。

对于带有和不带有显式长度参数的重载都是如此。当调用带长度参数的重载时,在返回指向内部缓冲区的指针之前,内部缓冲区可能会调整大小以适应增加的存储需求。

从这一评论中,很明显,这个问题完全是在问错误的事情。要了解原因,您需要了解GetBuffer()类成员家族的目的是什么:为修改暂时禁用CString的类不变量1的强制执行,直到通过调用ReleaseBuffer()成员之一再次建立它们。这方面的主要用例是与C代码接口(如Windows API)。

重要信息是:

如果计划直接修改存储的字符序列的内容,则只应调用
  • GetBuffer()
  • 在使用任何其他CString类成员2之前,对GetBuffer()的每个调用都必须与对ReleaseBuffer()的调用相匹配。请特别注意,operator PCXSTR()和析构函数是类成员
  • 只要您遵循该协议,受控字符序列将始终以null结尾

考虑到您的实际用例(Log.Print("%sn", myCstring.GetBuffer())),前面的内容都不适用。由于您不打算实际修改字符串内容,因此应该访问不可变的CString接口(例如GetString()或运算符PCXSTR())。这需要常量正确的函数签名(TCHAR const*TCHAR*)。如果不能做到这一点,请使用const_cast(如果可以确保被调用者不会改变缓冲区)。

这有几个好处:

  • 它在语义上是正确的。如果您只需要一个字符串视图,则不需要指向可变缓冲区的指针
  • 内容没有多余的副本。CString实现了写时复制语义。请求可变缓冲区需要复制共享实例的内容,即使您要在计算当前表达式后立即丢弃该副本
  • 不可变接口不能失败。调用operator PXCSTR()GetString()时不会引发异常

1相关不变量为:1受控字符序列总是以null结尾。2GetLength()返回受控序列中的字符数,不包括空终止符

2如果内容发生更改,则仅严格要求调用其中一个ReleaseBuffer()实现。从源代码来看,这一点通常并不明显,因此总是调用ReleaseBuffer()是安全的选择

文档没有结论。查看此处可用的ATL源(https://github.com/dblock/msiext/blob/d8898d0c84965622868b1763958b68e19fd49ba8/externals/WinDDK/7600.16385.1/inc/atl71/atlsimpstr.h-我不声称知道它们是否是官方的)看起来没有参数的GetBuffer()返回当前缓冲区,如果共享,则在之前克隆它。

另一方面,具有大小的GetBuffer(int)将检查(通过对PrepareWrite和可能的PrepareWrite2的调用)当前缓冲区大小是否大于请求的大小,如果不是,它将分配新的缓冲区,从而匹配MSDN描述。

顺便说一句,PrepareWrite在检查两个条件方面似乎变得很有创意:

PXSTR PrepareWrite( __in int nLength )
{
CStringData* pOldData = GetData();
int nShared = 1-pOldData->nRefs;  // nShared < 0 means true, >= 0 means false
int nTooShort = pOldData->nAllocLength-nLength;  // nTooShort < 0 means true, >= 0 means false
if( (nShared|nTooShort) < 0 )  // If either sign bit is set (i.e. either is less than zero), we need to copy data
{
PrepareWrite2( nLength );
}
return( m_pszData );
}

Windows API函数通常需要输入一定长度的字符缓冲区。然后使用GetBuffer(int)版本。以下代码片段说明了这一点以及GetBuffer()GetString()之间的区别,以及在调用GetBuffer():之后调用ReleaseBuffer()的重要性

CStringW FullName;
if(::GetModuleFileNameW(nullptr,FullName.GetBuffer(MAX_PATH), MAX_PATH) <= 0)
return 0;                                //GetBuffer() returns PXSTR
FullName.ReleaseBuffer();                     //Don't forget!
FullName = L"Path and Name: " + FullName;
std::wcout << FullName.GetString() << L"n";  //GetString() returns PCXSTR