C++学习(1):字符集和char、string

一、常见的编码集和编码格式

ASCII

ASCII字符集使用7位表示一个字符,包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。ASCII扩展字符集使用8位(bits)表示一个字符,共256字符(拓展了欧洲常用字符)。

GBK

GB2312:一个小于127的字符的意义与ASCII字符集相同(半角字符),两个大于127的字符连在一起时,就表示一个汉字。在这些编码里,还包括数学符号、罗马希腊的字母、日文的假名。在ASCII里本来就有的数字、标点、字母也重新编了两个字节长的编码(全角字符)。
GBK是对GB2312-80的扩展,增加了未收录的少数简化汉字、繁体汉字以及日韩汉字。采用多字节编码,每个字可以由1个、2个或4个字节组成。

Unicode

Unicode编码系统为表达任意语言的任意字符而设计。它使用4字节的数字来表达每个字母、符号,或者表意文字。每个数字代表唯一的至少在某种语言中使用的符号。
UTF-8:一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。
UTF-8使用一至四个字节为每个字符编码,128个US-ASCII字符只需一个字节编码,UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串。
Latin1:单字节编码,使用了单字节内的所有空间,向下兼容ASCII。

二、char

char是C++的基本字符类型,用于存储 ASCII 字符。一个char的空间应该确保可以存放机器基本字符集中任意字符对应的数字值。和int本身是带符号的不同,char实际上会表现为带符号的或者无符号的,具体哪一种由编译器决定。
char 类型是 C 和 C++ 中的原始字符类型。 char 类型可用于存储 ASCII 字符集或任何 ISO-8859 字符集中的字符,以及多字节字符的单个字节,例如 Shift-JIS 或 Unicode 字符集的 UTF-8 编码。
char8_t 和 char 类型的字符串称为“窄”字符串,即使用于编码 Unicode 或多字节字符。 编码为 UTF-16 的 Unicode 可以存储在 char16_t 类型中,而编码为 UTF-32 的 Unicode 可以存储在 char32_t 类型中。 这些类型和 wchar_t 类型的字符串都称为“宽”字符串。

三、string

标准库类型string表示可变长的字符序列。是和vector相似的容器,但用于专门保存字符。随机访问快,尾部插入/删除速度快。在stringfwd.h头文件中可以看到,string实际上是一个模板实参为char的类模板对象。

template<typename _CharT, typename _Traits = char_traits<_CharT>
		,typename _Alloc = allocator<_CharT> >
class basic_string;
    
// A string of @c char
  typedef basic_string<char>    string;   
  /// A string of @c wchar_t
  typedef basic_string<wchar_t> wstring;   
  typedef basic_string<char16_t> u16string; 
  /// A string of @c char32_t
  typedef basic_string<char32_t> u32string; 

char_traits是一个结构体模板,包含一些类型别名的定义和一些处理字符串的函数。

 template<typename _CharT>
struct char_traits
{
    typedef _CharT char_type;
    //有省略.....
    static _GLIBCXX14_CONSTEXPR void assign(char_type& __c1, const char_type& __c2)
    { __c1 = __c2; }
    //有省略.....
};

标准分配器allocator类,将分配内存和构造对象分离,避免不必要的构造浪费。继承自new_allocator,在new_allocator类模板中定义了分配内存、释放内存、构造对象、析构对象的函数。

template<typename _Tp>
class allocator: public __allocator_base<_Tp>{};
template<typename _Tp>
    using __allocator_base = __gnu_cxx::new_allocator<_Tp>;
template<typename _Tp>
    class new_allocator{};

1、basic_string.h文件中关于basic_string类模板的定义。

  • _Alloc_hider类型的结构体_M_dataplus,内有pointer类型成员_M_p,指向栈上数组_M_local_buf或堆上的内存
  • 字符串长度_M_string_length,
  • 字符串容量_M_allocated_capacity,
  • _CharT类型数组_M_local_buf。
template<typename _CharT, typename _Traits, typename _Alloc>
    class basic_string
    {
      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
	rebind<_CharT>::other _Char_alloc_type;
      typedef __gnu_cxx::__alloc_traits<_Char_alloc_type> _Alloc_traits;

      // Types:
    public:
      //有省略。。。。。
    private:
      // type used for positions in insert, erase etc.
      //使用空类优化,allocator_type 是一个空类,将_M_p放在allocator_type子类对象_M_dataplus内可以节省空间
      //如果定义成两个成员,由于对齐占用16个字节,优化后将只占用8个字节
      struct _Alloc_hider : allocator_type // TODO check __is_final
      {
			pointer _M_p; // The actual data.
      };

      _Alloc_hider	_M_dataplus;
      size_type		_M_string_length;

      enum { _S_local_capacity = 15 / sizeof(_CharT) };//在string下也就是15,小字符串用的容量
//共用16字节,sizeof(string)=32,sizeof(vector)=24,因为vector没有小字符串优化,都是分配在堆上的,
      union
      {//字符串长度小于16字节时,存在_M_local_buf中,否则new到堆上
			_CharT           _M_local_buf[_S_local_capacity + 1];//额外的一个字节来存放'/0',保证c_str()的转化
			size_type        _M_allocated_capacity;//超过15才使用,分配的容量
      };
      //有省略。。。。。
      };

size()和length():返回_M_string_length,即字符串长度,不包括终止字符。

size_type size() const _GLIBCXX_NOEXCEPT
{ return _M_string_length; }
size_type length() const _GLIBCXX_NOEXCEPT
{ return _M_string_length; }

at():先对索引值范围做出判断,如果超出字符串大小,抛出错误,否则返回_M_data()的[]运算符结果,_M_data()返回_M_dataplus._M_p,指针的下标即指针移动后指向的元素。

reference at(size_type __n)
{
	if (__n >= size())
	  __throw_out_of_range_fmt(__N("basic_string::at: __n "
				       "(which is %zu) >= this->size() "
				       "(which is %zu)"),
				   __n, this->size());
	return _M_data()[__n];//指针的下标即指针移动后指向的元素
}

capacity():_M_is_local()判断_M_p是否指向数组_M_local_buf,如果是代表字符串大小小于15,字符数据保存在_M_local_buf中,字符串容量使用_S_local_capacity(15),否则字符数据保存在堆上分配的内存中,容量使用_M_allocated_capacity。

size_type capacity() const _GLIBCXX_NOEXCEPT
{
	return _M_is_local() ? size_type(_S_local_capacity)
	                     : _M_allocated_capacity;
}

push_back()

void push_back(_CharT __c)
{
	const size_type __size = this->size();
	if (__size + 1 > this->capacity())
	    this->_M_mutate(__size, size_type(0), 0, size_type(1));
	traits_type::assign(this->_M_data()[__size], __c);
	this->_M_set_length(__size + 1);
}

2、默认构造函数,字符串长度设置为零,构造一个空string。

basic_string()
_GLIBCXX_NOEXCEPT_IF(is_nothrow_default_constructible<_Alloc>::value)
: _M_dataplus(_M_local_data())//
{ _M_set_length(0); }
pointer _M_local_data()
{return std::pointer_traits<pointer>::pointer_to(*_M_local_buf);}
void _M_set_length(size_type __n)
{
	_M_length(__n);
	traits_type::assign(_M_data()[__n], _CharT());//分配终止字符
}
void _M_length(size_type __length)
{ _M_string_length = __length; }
pointer _M_data() const
{ return _M_dataplus._M_p; }
void _M_data(pointer __p)
{ _M_dataplus._M_p = __p; }

3、接受C风格字符串为参数的构造函数

basic_string(const _CharT* __s, const _Alloc& __a = _Alloc()) : _M_dataplus(_M_local_data(), __a)
{ _M_construct(__s, __s ? __s + traits_type::length(__s) : __s+npos); }

_M_construct:在第一个while循环中依次复制从beg到end的所有字符,如果长度小于15,容量依旧是15,_M_dataplus._M_p指向栈上数组_M_local_buf;如果用完了所有空间(即字符长度大于15)但是还没有赋值完成,在第二个while循环中调用_M_create分配更多内存,将之前复制的数据拷贝到新空间,接着复制,循环直到所有字符都复制到string中,_M_dataplus._M_p指向堆上新分配的内存。

//用于string::iterator、_CharT*等
 template<typename _CharT, typename _Traits, typename _Alloc>
    template<typename _InIterator>
      void basic_string<_CharT, _Traits, _Alloc>::
      _M_construct(_InIterator __beg, _InIterator __end,
		   std::input_iterator_tag)
      {
		size_type __len = 0;
		size_type __capacity = size_type(_S_local_capacity);//15
	    //从头到尾依次复制
		while (__beg != __end && __len < __capacity)
		{
		    _M_data()[__len++] = *__beg;//指针的下标即指针移动后指向的元素,是引用值
		    ++__beg;
		}
		__try
		  {
		    while (__beg != __end)
		    {//用完了所有空间但是还没有赋值完成调用_M_create分配更多内存,
		    
				if (__len == __capacity)
				{
				    // Allocate more space.分配更多空间
				    __capacity = __len + 1;
				    pointer __another = _M_create(__capacity, __len);
				    this->_S_copy(__another, _M_data(), __len);//将之前复制的数据拷贝到新空间
				    _M_dispose();
				    _M_data(__another);//令指针指向新空间
				    _M_capacity(__capacity);
			  }
			  _M_data()[__len++] = *__beg;
			  ++__beg;
		      }
		  }
		__catch(...)
		  {
		    _M_dispose();//发生错误即释放
		    __throw_exception_again;
		  }
		_M_set_length(__len);//赋值长度,构造终止字符
      }

_M_create():大于旧尺寸,分配两倍空间;确认新尺寸不超过max_size();返回分配的堆上空间地址,在构造函数中赋值给_M_dataplus._M_p,+1的空间留给’/0’。

template<typename _CharT, typename _Traits, typename _Alloc>
typename basic_string<_CharT, _Traits, _Alloc>::pointer basic_string<_CharT, _Traits, _Alloc>::
    _M_create(size_type& __capacity, size_type __old_capacity)
{
      if (__capacity > max_size())//不能分配比max_size()更大的内存,否则抛出错误
		  std::__throw_length_error(__N("basic_string::_M_create"));

      if (__capacity > __old_capacity && __capacity < 2 * __old_capacity)//大于旧尺寸小于两倍旧尺寸
	{
	  __capacity = 2 * __old_capacity;//分配两倍旧尺寸
	  // Never allocate a string bigger than max_size.
	  if (__capacity > max_size())//确认新尺寸不超过max_size()
	      __capacity = max_size();
	}
      //返回分配的空间地址,在构造函数中赋值给_M_p,+1的空间留给'/0'
      return _Alloc_traits::allocate(_M_get_allocator(), __capacity + 1);
}
allocator_type& _M_get_allocator()
{ return _M_dataplus; }
static pointer allocate(_Alloc& __a, size_type __n)
{ return __a.allocate(__n); }

_M_dispose():首先判断分配的空间在堆上还是在栈上,在栈上什么也不做,在堆上最终调用_Alloc_traits::deallocate释放内存。

void _M_dispose()
{
	if (!_M_is_local())
	    _M_destroy(_M_allocated_capacity);
}
void _M_destroy(size_type __size) throw()
{ 
	_Alloc_traits::deallocate(_M_get_allocator(), _M_data(), __size + 1); 
}

4、拷贝构造函数

basic_string(const basic_string& __str): _M_dataplus(_M_local_data(), _Alloc_traits::_S_select_on_copy(__str._M_get_allocator()))
{ _M_construct(__str._M_data(), __str._M_data() + __str.length()); }

5、拷贝赋值运算符

basic_string& operator=(const basic_string& __str)
{
	if (_Alloc_traits::_S_propagate_on_copy_assign())
	  {
	    if (!_Alloc_traits::_S_always_equal() && !_M_is_local()
		&& _M_get_allocator() != __str._M_get_allocator())
	      {
		// Propagating allocator cannot free existing storage so must
		// deallocate it before replacing current allocator.
		if (__str.size() <= _S_local_capacity)
		  {
		    _M_destroy(_M_allocated_capacity);
		    _M_data(_M_local_data());
		    _M_set_length(0);
		  }
		else
		  {
		    const auto __len = __str.size();
		    auto __alloc = __str._M_get_allocator();
		    // If this allocation throws there are no effects:
		    auto __ptr = _Alloc_traits::allocate(__alloc, __len + 1);
		    _M_destroy(_M_allocated_capacity);
		    _M_data(__ptr);
		    _M_capacity(__len);
		    _M_set_length(__len);
		  }
	      }
	    std::__alloc_on_copy(_M_get_allocator(), __str._M_get_allocator());
	  }
	return this->assign(__str);
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值