VS2010 std::string 源码分析

分配内存原理分配内存原理

union _Bxty
{	// storage for small buffer or pointer to larger one
	_Elem _Buf[_BUF_SIZE];
	_Elem *_Ptr;
	char _Alias[_BUF_SIZE];	// to permit aliasing
} _Bx;

这个_Bxty是一个union。当分配字符串所需内存小于_BUF_SIZE_Buf存放我们的字符串,当然也不一定是字符串,也可以放int数组这种东西。当分配内存大于_BUF_SIZE_Ptr由内存分配器_ALLOCATOR分配,然后存放我们的字符串。

类构成关系

typedef basic_string<char, char_traits<char>, allocator<char> > string;
template<class _Elem>
	struct char_traits
		: public _Char_traits<_Elem, long>
	{	// properties of a string or stream unknown element
	};

xmemory文件:

#define _ALLOCATOR	allocator
template<class _Ty>
	class _ALLOCATOR
		: public _Allocator_base<_Ty>
	{	// generic allocator for objects of class _Ty
...
}
		// TEMPLATE CLASS basic_string
template<class _Elem,
	class _Traits,
	class _Ax>
	class basic_string
		: public _String_val<_Elem, _Ax>
	{	// null-terminated transparent array of elements
...
}
		// TEMPLATE CLASS _String_val
template<class _Elem,
	class _Alloc>
	class _String_val
		: public _Container_base
	{	// base class for basic_string to hold data
...
	union _Bxty
		{	// storage for small buffer or pointer to larger one
		_Elem _Buf[_BUF_SIZE];
		_Elem *_Ptr;
		char _Alias[_BUF_SIZE];	// to permit aliasing
		} _Bx;
	size_type _Mysize;	// current length of string
	size_type _Myres;	// current storage reserved for string
	_Alty _Alval;	// allocator object for strings
}
typedef _Container_base12 _Container_base;
struct _CRTIMP2_PURE _Container_base12
	{	// store pointer to _Container_proxy
...
}

从实例构造说起

	string str5("abcdefghijklmn12345");
	string str("abc");
	str = "abcdefghijklmn12345";
	basic_string(const _Elem *_Ptr)
		: _Mybase()
		{	// construct from [_Ptr, <null>)
		_Tidy();
		assign(_Ptr);
		}
	_String_val(_Alty _Al = _Alty())
		: _Alval(_Al)
		{	// construct allocator from _Al
		typename _Alloc::template rebind<_Container_proxy>::other
			_Alproxy(_Alval);
		this->_Myproxy = _Alproxy.allocate(1);
		_Cons_val(_Alproxy, this->_Myproxy, _Container_proxy());
		this->_Myproxy->_Mycont = this;
		}

上面这一大堆就是stl里面公用的那一套机制。暂且不表。

	void _Tidy(bool _Built = false,
		size_type _Newsize = 0)
		{	// initialize buffer, deallocating any storage
		if (!_Built)
			;
		else if (this->_BUF_SIZE <= this->_Myres)
			{	// copy any leftovers to small buffer and deallocate
			_Elem *_Ptr = this->_Bx._Ptr;
			if (0 < _Newsize)
				_Traits::copy(this->_Bx._Buf, _Ptr, _Newsize);
			this->_Alval.deallocate(_Ptr, this->_Myres + 1);
			}
		this->_Myres = this->_BUF_SIZE - 1;
		_Eos(_Newsize);
		}

其实我一直在想stl里面的_Tidy函数到底干啥的。tidy这个单词有整洁的,整齐的,使整洁,使整齐,整理之意。那这里呢。看这句注释// initialize buffer, deallocating any storage 意思大概是初始化内存,这里用的buffer,即缓存,反正也是内存吧,然后释放所有的存储。来具体看看代码。_Built这参数的含义应该就是是否使用了堆内存机制,即是否使用了this->_Bx._Ptr。若是使用了,就把堆内存考到栈内存this->_Bx._Buf,再把堆内存释放掉,当然了,刚构造一个basic_string的时候,肯定还未使用堆内存机制,无论用于初始化的字符串有多长。this->_Myres是说当前保留了几个字节可以用来分配内存,当然还有一个字节用来保存0。然后把_Newsize处置0。

	_Myt& assign(const _Elem *_Ptr, size_type _Count)
		{	// assign [_Ptr, _Ptr + _Count)
		if (_Inside(_Ptr))
			return (assign(*this, _Ptr - _Myptr(), _Count));	// substring

		if (_Grow(_Count))
			{	// make room and assign new stuff
			_Traits::copy(_Myptr(), _Ptr, _Count);
			_Eos(_Count);
			}
		return (*this);
		}

这里_Inside得false,后面可以讨论得true的情况

bool _Grow(size_type _Newsize,	bool _Trim = false)
{	// ensure buffer is big enough, trim to size if _Trim is true
if (max_size() < _Newsize)//情况①:这种情况崩溃,分配字符数太大了。
	_Xlen();	// result too long
if (this->_Myres < _Newsize)//情况②:要分配的大于能够为字符串提供内存大小,重新分配内存
	_Copy(_Newsize, this->_Mysize);	// reallocate to grow
else if (_Trim && _Newsize < this->_BUF_SIZE)//情况③:砍掉不要的字符串,只有栈内存机制可行
	_Tidy(true,	// copy and deallocate if trimming to small string
		_Newsize < this->_Mysize ? _Newsize : this->_Mysize);
else if (_Newsize == 0)//情况④
		_Eos(0);	// new size is zero, just null terminate
return (0 < _Newsize);	// return true only if more work to do情况⑤:当然是不需要分配就够了,
//比如本来”abc”,现在”abcde”,没超过16字节,依旧栈内存机制,无需再分配内存。
}

着重讲一下情况②:

void _Copy(size_type _Newsize, size_type _Oldlen)
		{	// copy _Oldlen elements to newly allocated buffer
		size_type _Newres = _Newsize | this->_ALLOC_MASK;
		if (max_size() < _Newres)//情况①
			_Newres = _Newsize;	// undo roundup if too big
		else if (this->_Myres / 2 <= _Newres / 3)//情况②
			;
		else if (this->_Myres <= max_size() - this->_Myres / 2)//情况③
			_Newres = this->_Myres
				+ this->_Myres / 2;	// grow exponentially if possible
		else//情况④
			_Newres = max_size();	// settle for max_size()

		_Elem *_Ptr;
		_TRY_BEGIN
			_Ptr = this->_Alval.allocate(_Newres + 1);
		_CATCH_ALL
			_Newres = _Newsize;	// allocation failed, undo roundup and retry
			_TRY_BEGIN
				_Ptr = this->_Alval.allocate(_Newres + 1);
			_CATCH_ALL
			_Tidy(true);	// failed again, discard storage and reraise
			_RERAISE;
			_CATCH_END
		_CATCH_END

		if (0 < _Oldlen)
			_Traits::copy(_Ptr, _Myptr(), _Oldlen);	// copy existing elements
		_Tidy(true);
		this->_Bx._Ptr = _Ptr;
		this->_Myres = _Newres;
		_Eos(_Oldlen);
		}

string str5("abcdefghijklmn12345");这句话会调用到上面这个函数,那么_Newsize为19,毕竟"abcdefghijklmn12345"是有19个字符。_Oldlen为0。大概有这么几种情况,会调用这个函数:

  1. 刚初始化的时候,字符占字节数超过_BUF_SIZE
  2. 刚初始化,字符占字节数不超_BUF_SIZE,后来重新赋值的字符串占字节数超过_BUF_SIZE
  3. 刚初始化,字符占字节数超过_BUF_SIZE为sizeA,后来重新赋值的字符串占字节数sizeB超过sizeA

这个函数叫做_Copy,意思就是把之前内存字符串拷贝到现在内存处,如果之前有分配内存,比如上面情况1,2,现内存是从新分配的,现内存大小取值由上面标注四种情况,情况②④不太理解,暂时不表。反正重新分配了内存,拷贝内存,_Tidy(true)把栈内存置null。然后修改_Bx._Ptr_Myres,然后_Eos(_Oldlen)置null,这里是_Oldlen,毕竟还没有把现字符串赋值进来。

回到assign函数。现在_Grow调用成功后,有下面两句。

_Traits::copy(_Myptr(), _Ptr, _Count);

_Eos(_Count);

这两句也简单。不过我在想,_Grow里面调用了_Copy,把之前内存拷到现内存,现在又把当前字符串赋值到现内存,那么把原先字符串拷到现内存是不是就多此一举了。

其他一些组件

迭代器

			// TEMPLATE CLASS _String_const_iterator
template<class _Elem,
	class _Traits,
	class _Alloc>
	class _String_const_iterator
		: public _Iterator012<random_access_iterator_tag,
			typename _Alloc::value_type,
			typename _Alloc::difference_type,
			typename _Alloc::const_pointer,
			typename _Alloc::const_reference,
			_Iterator_base>
	{	// iterator for nonmutable string
public:
	typedef _String_const_iterator<_Elem, _Traits, _Alloc> _Myiter;
	typedef basic_string<_Elem, _Traits, _Alloc> _Mystr;
	typedef random_access_iterator_tag iterator_category;

	typedef typename _Alloc::value_type value_type;
	typedef typename _Alloc::difference_type difference_type;
	typedef typename _Alloc::const_pointer pointer;
	typedef typename _Alloc::const_reference reference;
	_String_const_iterator(pointer _Parg, const _Container_base *_Pstring)
		{	// construct with pointer _Parg
		this->_Adopt(_Pstring);
		this->_Ptr = _Parg;
		}
...//这里无非一些 operator*,->,++,--之类的东西。
pointer _Ptr;	// pointer to element in string
};
template<class _Elem,
	class _Traits,
	class _Alloc>
	class _String_iterator
		: public _String_const_iterator<_Elem, _Traits, _Alloc>
	{	// iterator for mutable string
...
}

字符特性Traits

iosfwd文件:

template<class _Elem,
	class _Int_type>
	struct _Char_traits
	{	// properties of a string or stream element
...//这里面一些compare,length,copy,find,move函数
}
		// TEMPLATE STRUCT char_traits
template<class _Elem>
	struct char_traits
		: public _Char_traits<_Elem, long>
	{	// properties of a string or stream unknown element
...
	};
template<>
	struct char_traits<wchar_t>
	{	// properties of a string or stream wchar_t element
...
}
template<> struct char_traits<unsigned short>
	{	// properties of a string or stream unsigned short element
...
}
template<> struct char_traits<char>
	{	// properties of a string or stream char element
...
}

其他一些重要函数

_Elem *_Myptr()
{	// determine current pointer to buffer for mutable string
return (this->_BUF_SIZE <= this->_Myres ? this->_Bx._Ptr
		: this->_Bx._Buf);
}

这个函数就是返回到底是栈内存还是堆内存。

_Myt& append(const _Myt& _Right,size_type _Roff,size_type _Count)//_Roff从第几个字符开始复制
{	// append _Right [_Roff, _Roff + _Count)
	if (_Right.size() < _Roff)			//一共三个字符,你却从第四个开始复制,over
		_Xran();	// _Roff off end
	size_type _Num = _Right.size() - _Roff;
	if (_Num < _Count)
		_Count = _Num;	// trim _Count to size
	if (npos - this->_Mysize <= _Count)//npos为(size_type)(-1),判断是否复制后超过最大字符数
		_Xlen();	// result too long

	if (0 < _Count && _Grow(_Num = this->_Mysize + _Count))//增长内存(_Grow里面判断是否真的需要增)
	{	// make room and append new stuff	//把_Right字符串复制到本字符串尾端
		_Traits::copy(_Myptr() + this->_Mysize,
			_Right._Myptr() + _Roff, _Count);
		_Eos(_Num);
	}
	return (*this);
}
  #define _STRING_ITERATOR(ptr)			iterator(ptr, this)
	iterator begin()
		{	// return iterator for beginning of mutable sequence
		return (_STRING_ITERATOR(_Myptr()));
		}

返回一个_String_iterator变量。

	const _Elem *c_str() const
		{	// return pointer to null-terminated nonmutable array
		return (_Myptr());
		}

返回保存字符串的内存的地址,栈内存或堆内存。

	void clear()
		{	// erase all
		_Eos(0);
		}

这里并没有释放内存,只是把第一个字符置null。

	_Myt& erase(size_type _Off = 0,size_type _Count = npos)
	{	// erase elements [_Off, _Off + _Count)
		if (this->_Mysize < _Off)
			_Xran();	// _Off off end
		if (this->_Mysize - _Off < _Count)
			_Count = this->_Mysize - _Off;	// trim _Count
		if (0 < _Count)
		{	// move elements down
			_Traits::move(_Myptr() + _Off, _Myptr() + _Off + _Count,
				this->_Mysize - _Off - _Count);
			size_type _Newsize = this->_Mysize - _Count;
			_Eos(_Newsize);
		}
		return (*this);
	}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值