GetBuffer 与 ReleaseBuffer

来源:互联网 发布:linux系统服务器配置 编辑:程序博客网 时间:2024/06/03 22:57

之前对GetBuffer与ReleaseBuffer的理解有误

也是因为受了网上的误导。ReleaseBuffer并不会释放空间!

 

借用一句话,源代码面前没有秘密。

 

Getbuffer(int nMinBufferLength)

 

函数作用:分配空间并返回字符串首地址指针。

若为默认参数,则

若nMinBufferLength比当前已分配空间小,则当前分配空间不变。

若比当前分配空间大,则分配不小于nMinBufferLength的空间。

 

VC9中会每次分配空间的时候会多分配一点避免频繁分配空间,而VC6中是指定多少就是多少,这点最好通过测试代码验证一下。造成差异的原因是MFC库的版本不同。

 

ReleaseBuffer(int nNewLength)

 

函数作用:更新字符串的长度

若nNewLength为默认参数-1,则自动寻找结束符更新字符串长度,若没有结束符,则在已分配空间的末尾加上这个结束符。

若nNewLength比当前字符串的长度小,则当前字符串被截断,若比当前字符串的长度大,字符串的长度被更新为新设置的长度。也就是说:nNewLength如果被设置了一个值,字符串的长度就被更新为这个长度。要注意这个值不能超过已分配空间的长度!

 

GetBuffer VC9下的源码剖析,开个程序打开atlsimpstr.h打断点跑跑就很清楚了

 

// 不传入参数的时候调用这个

PXSTR GetBuffer()

{

CStringData* pData = GetData();

if( pData->IsShared() )

{

Fork( pData->nDataLength );//  pData->nDataLength 当前字符串长度 Fork 详见如下注释 大体作用是新分配一片空间,并将本字符串与新分配的空间Attach

}

 

return( m_pszData );// 返回字符串首地址指针

}

 

ATL_NOINLINE void Fork( _In_ int nLength )

{

CStringData* pOldData = GetData();// 保存字符串内容

int nOldLength = pOldData->nDataLength;// 保存字符串的长度

CStringData* pNewData = pOldData->pStringMgr->Clone()->Allocate( nLength, sizeof( XCHAR ) ); // 新分配nLength的空间

if( pNewData == NULL )

{

ThrowMemoryException();// 抛出内存异常

}

int nCharsToCopy = ((nOldLength < nLength) ? nOldLength : nLength)+1;  // Copy '/0' // 故如果为空字符串 调用GetBuffer 会分配7*sizeof(XCHAR)的空间

CopyChars( PXSTR( pNewData->data() ), nCharsToCopy, 

PCXSTR( pOldData->data() ), nCharsToCopy ); // 拷贝新字符串到旧字符串

pNewData->nDataLength = nOldLength; // 设置新字符串的长度为旧的长度 这就是为什么Getbuffer 不会改变字符串长度 (只改变分配的空间的长度)

pOldData->Release();// 代码如下 减少引用计数

Attach( pNewData );// 代码如下 Attach新数据到本字符串 代码很清楚 将私有成员变量 字符串的首地址指针指向刚才分配的地址

}

 

void Release() throw()

{

ATLASSERT( nRefs != 0 );

 

if( _AtlInterlockedDecrement( &nRefs ) <= 0 )

{

pStringMgr->Free( this );

}

}

 

void Attach( _In_ CStringData* pData ) throw()

{

m_pszData = static_cast< PXSTR >( pData->data() );

}

 

 

// GetBuffer有参数 则调用下面这个函数

// _Ret_cap_ 分配内存空间

_Ret_cap_(nMinBufferLength + 1) PXSTR GetBuffer( _In_ int nMinBufferLength )

{

return( PrepareWrite( nMinBufferLength ) );

}

 

 

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

// 上面有任意一个数字小于0的话 也就是说 字符串引用数大于1 或者GetBuffer(nLength)的nLength > 已分配空间

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 );

}

ATL_NOINLINE void PrepareWrite2( _In_ int nLength )

{

CStringData* pOldData = GetData();

// 若字符串的长度大于要分配的长度

if( pOldData->nDataLength > nLength )

{

// 将要分配的长度设为字符串当前长度 故GetBuffer 不会让分配的空间变少

nLength = pOldData->nDataLength;

}

// 若引用计数大于1 不能改变被引用的字符串 故要重新分配新的空间 再将本字符串Attach过去

if( pOldData->IsShared() )

{

Fork( nLength );

}

// 若分配的长度小于 传入的需要分配的长度 则重新按如下的策略分配新的空间

// 故不会分配得来一个更小的空间(如上引用计数大于1的时候除外)

else if( pOldData->nAllocLength < nLength )

{

// Grow exponentially, until we hit 1K.

int nNewLength = pOldData->nAllocLength;

if( nNewLength > 1024 )

{

nNewLength += 1024;

}

else

{

nNewLength *= 2;

}

if( nNewLength < nLength )

{

nNewLength = nLength;

}

Reallocate( nNewLength );

}

}

 

// 这个就不注释拉

ATL_NOINLINE void Reallocate( _In_ int nLength )

{

CStringData* pOldData = GetData();

ATLASSERT( pOldData->nAllocLength < nLength );

IAtlStringMgr* pStringMgr = pOldData->pStringMgr;

if ( pOldData->nAllocLength >= nLength || nLength <= 0)

{

ThrowMemoryException();

return;

}

CStringData* pNewData = pStringMgr->Reallocate( pOldData, nLength, sizeof( XCHAR ) );

if( pNewData == NULL )

{

ThrowMemoryException();

}

Attach( pNewData );

}

 

 

以下为一些测试代码基本没什么参考价值

#include <atlstr.h>

#include <iostream>

using namespace std;

void vOutPutStrLengthInfo(const CString &p_strOutPut)

{

cout<<"Str Length: "<<p_strOutPut.GetLength()<<endl;

cout<<"Str AllocLength: "<<p_strOutPut.GetAllocLength()<<endl;

cout<<"Str Contain: "<<p_strOutPut<<endl;

cout<<endl;

}

int main()

{

// 以下代码直接拷贝进VC9空工程的CPP里运行,看输出值

CString l_strTestBuffer = "Buffer";

 

vOutPutStrLengthInfo(l_strTestBuffer);

 

l_strTestBuffer.GetBuffer(100);

vOutPutStrLengthInfo(l_strTestBuffer);

 

l_strTestBuffer.ReleaseBuffer();

vOutPutStrLengthInfo(l_strTestBuffer);

 

l_strTestBuffer.ReleaseBuffer(50);

vOutPutStrLengthInfo(l_strTestBuffer);

 

l_strTestBuffer.ReleaseBuffer(3);

vOutPutStrLengthInfo(l_strTestBuffer);

 

TCHAR *l_pChar = l_strTestBuffer.GetBufferSetLength(50);

vOutPutStrLengthInfo(l_strTestBuffer);

 

cout<<l_strTestBuffer[4]<<endl;

l_pChar[3] = _T('C');// 这里已经被l_strTestBuffer.ReleaseBuffer(3)插入了一个'/0',用指针修改这里的内容,则又可将字符串输出

cout<<l_strTestBuffer<<endl;

 

// 这里要出错

//l_strTestBuffer.ReleaseBuffer(200);

 

return 0;

}

 

未完...待补上ReleaseBuffer的部分