没有size参数的CString :: GetBuffer()有什么作用?

时间:2018-09-11 19:29:53

标签: c++ mfc

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

我正在查看不需要的情况下不想更改的现有C ++(Visual Studio)代码,但是我需要知道CString::GetBuffer()的预期行为。如果有人可以解释它或为我指出一些文档,我将不胜感激。

4 个答案:

答案 0 :(得分:6)

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

return( m_pszData );

因此,它仅返回指向基础字符缓冲区的指针。 (它还会检查内部数据是否共享,并首先派生/复制)。

代码在atlsimpstr.h

完整功能:

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

    return( m_pszData );
}

答案 1 :(得分:1)

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

另一方面,具有大小的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 );
}

答案 2 :(得分:1)

tl;dr

致电CString::GetString()


这是出于错误原因而提出的错误问题。只是为了摆脱它,这是documentation的答案:

  

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

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

this comment可以明显看出,问题完全是在请求错误的东西。要了解原因,您需要了解GetBuffer()类成员家族的目的是:暂时 禁止执行CString的类不变量 1 < / sup>修改 ,直到通过调用ReleaseBuffer()成员之一再次建立它们。这样做的主要用例是与C代码(例如Windows API)接口。

重要信息是:

  • GetBuffer()仅在您打算直接修改存储的字符序列的内容时才应调用。
  • 在使用任何其他GetBuffer()类成员 2 之前,对ReleaseBuffer()的每次调用都必须与对CString的调用相匹配。特别注意,operator PCXSTR()和析构函数是类成员。
  • 只要您遵循该协议,受控字符序列将始终为空终止。

鉴于您的实际用例(Log.Print("%s\n", myCstring.GetBuffer())),上述任何一个都不适用。由于您实际上并不打算修改字符串内容,因此应该访问不可变的CString接口(例如GetString()operator PCXSTR())。这需要使用const正确的函数签名(TCHAR const*TCHAR*)。失败的话,请使用const_cast(如果可以确保被调用者不会突变缓冲区)。

这有几个好处:

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

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

2 如果更改了内容,则仅严格要求调用ReleaseBuffer()实现之一。从源代码看,这通常通常不会立即显现出来,因此总是调用ReleaseBuffer()是安全的选择。

答案 3 :(得分:1)

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

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