ASCII字符集使用7位表示一个字符,包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。ASCII扩展字符集使用8位(bits)表示一个字符,共256字符(拓展了欧洲常用字符)。
GB2312:一个小于127的字符的意义与ASCII字符集相同(半角字符),两个大于127的字符连在一起时,就表示一个汉字。在这些编码里,还包括数学符号、罗马希腊的字母、日文的假名。在ASCII里本来就有的数字、标点、字母也重新编了两个字节长的编码(全角字符)。
GBK是对GB2312-80的扩展,增加了未收录的少数简化汉字、繁体汉字以及日韩汉字。采用多字节编码,每个字可以由1个、2个或4个字节组成。
Unicode编码系统为表达任意语言的任意字符而设计。它使用4字节的数字来表达每个字母、符号,或者表意文字。每个数字代表唯一的至少在某种语言中使用的符号。
UTF-8:一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,这使得原来处理ASCII字符的软件无须或只须做少部份修改,即可继续使用。
UTF-8使用一至四个字节为每个字符编码,128个US-ASCII字符只需一个字节编码,UTF-8是ASCII的一个超集。因为一个纯ASCII字符串也是一个合法的UTF-8字符串。
Latin1:单字节编码,使用了单字节内的所有空间,向下兼容ASCII。
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表示可变长的字符序列。是和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类模板的定义。
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);
}