在C语言中,string是一个规范库类(class),用于处理字符串,它供给了一种更高级、更便捷的字符串操作方法,string类供给了一系列成员函数和重载运算符,以便于对字符串进行操作和处理。
一、string类
在学习 string 前,咱们无妨先来了解一下 string 类到底是什么,有什么用呢?咱们先来了解一下根本的概念吧
C++规范库都是英语解说。咱们也应该试着去适应,不懂的能够查阅。当然,在这里我就直接给出翻译,首要是以下内容:
字符串是表示字符序列的类;
- 规范的字符串类供给了对此类目标的支持,其接口类似于规范字符容器的接口,但添加了专门用于操作单字节字符字符串的规划特性。
- string类是运用char(即作为它的字符类型,运用它的默许char_traits和分配器类型(关于模板的更多信息,请参阅basic_string)。
- string类是basic_string模板类的一个实例,它运用char来实例化basic_string模板类,并用char_traits和allocator作为basic_string的默许参数(根于更多的模板信息请参阅basic_string)。
- 留意,这个类独立于所运用的编码来处理字节:假如用来处理多字节或变长字符(如UTF-8)的序列,这个类的所有成员(如长度或巨细)以及它的迭代器,将仍然按照字节(而不是实际编码的字符)来操作。
二、string的常用见用法
2.1 string目标的结构
2.1.1string目标的结构的运用方法
最为常用的无非便是咱们用串string来结构一个目标,也便是存储一个字符,常用的方法有如下几点:
- string()——结构空的string类目标,即空字符串;
- string(const char s)——用 char来结构string类目标;**
- string(size_t n, char c)——string类目标中包括n个字符c;
- string(const string&s)——复制结构函数。
下面是运用方法所对应的实例,帮助更好的了解其用法。
三、string常用结构的底层完结
3.1 初建结构
咱们经过上述的结构,不难发现也不难了解string的底层其实便是一个字符指针,该指针指向一个数组。当然,咱们还需求两个变量来保护其有效长度(_size) 和数组容量(_capacity) 。
其次,咱们自己完结的string类为了区别std命名空间,咱们可自己设置一个命名空间。处型的模拟完结如下:
namespace gtm
{
class string
{
public:
//string()
// :_str(new char[1])
// , _size(0)
// ,_capacity(0)
//{
//}
//string(const char* str)
// :_str(new char[strlen(str) + 1]) //三次strlen函数,效率低。
// ,_size(strlen(str))
// ,_capacity(strlen(str))
//{
// strcpy(_str, str);
//}
// 不再运用strlen函数,初始化列表与变量声明次序固定
string(const char* str = "") //默许空串。留意:空串是以 \0 结束
{
_size = strlen(str);
_capacity = _size;
_str = new char[_size + 1];
strcpy(_str, str);
}
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
3.2 返回巨细和容量
这两个部分,是比较简略完结的两部分。同时也是较为常用的两部分。详细如下:
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
3.3 复制结构和赋值重载
这两部分较为杂乱的两部分。其间均需求深复制去完结完结,而浅复制是不能够的。留意:复制结构运用一个已定义变量去初始化另一个变量,赋值重载是两个已定义变量进行赋值。
详细完结如下:
//深复制
//string(const string& s)
// :_str(new char[s._capacity+1])
// ,_size(s._size)
// ,_capacity(s._capacity)
//{
// strcpy(_str, s._str);
//}
void swap(string& tmp)
{
//调用大局的swap
::swap(_str, tmp._str);
::swap(_size, tmp._size);
::swap(_capacity, tmp._capacity);
}
//凭借变量tmp
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
string tmp(s._str);
swap(tmp);
}
//赋值
//string& operator=(const string& s)
//{
// if(this == &s)
// {
// return *this;
// }
// //先开空间复制数据,以防new失利销毁本来的空间
// char* tmp = new char[s._capacity + 1];
// strcpy(tmp, s._str);
// delete[] _str;
// _str = tmp;
// _size = s._size;
// _capacity = s._capacity;
// return *this;
// //delete[] _str;
// //_str = new char[s._capacity + 1];
// //strcpy(_str, s._str);
// //_size = s._size;
// //_capacity = s._capacity;
// return *this;
//}
//string& operator=(const string& s)
//{
// if(this == &s)
// {
// return *this;
// }
// string tmp(s._str);
// swap(tmp);
// return *this;
//}
string& operator=(string s)
{
if (this == &s)
{
return *this;
}
swap(s);
return *this;
}
上述的辅佐重载咱们奇妙地凭借了临时变量s。当赋值完结后,出了作用域s会自动调用戏后进行销毁,这里是需求反复了解的。
3.4 扩容(reserve)
咱们可简略的了解reserve为扩容(扩容的前提为要求的容量比本来的大),但是咱们要记得把字符数组华夏有的内容复制过来,并且释放之前所动态拓荒的空间。详细完结如下:
void reserve(size_t capacity)
{
if (capacity > _capacity)
{
char* tmp = new char[capacity + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = capacity;
}
}
3.5 刺进(push_back、append、operator+=、insert)
刺进的完结,首要的点便是是否要进行扩容。其次,当咱们完结push_back和append后,其他的均可复用这两个结构进行完结。详细完结如下:
void push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size >= _capacity * 2 ? len + _size : _capacity * 2);
}
strcpy(_str + _size, str);
_size += len;
}
void append(const string& s)
{
append(s._str);
}
void append(int n, char ch)
{
reserve(_size + n);
for (int i = 0; i < n; i++)
{
push_back(ch);
}
}
string& operator+= (char ch)
{
push_back(ch);
return *this;
}
string& operator+= (const char* str)
{
append(str);
return *this;
}
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//留意,当运管用一个是有符号,另一个是无符号时,有符号的运管用会强制类型转换为无符号数。pos等于0的位置刺进,end--后为超大数据,会犯错。
//int end = _size;
//while (end >= (int)pos)
//{
// _str[end + 1] = _str[end];
// end--;
//}
size_t end = _size+1;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[pos] = ch;
_size++;
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size >= _capacity * 2 ? len + _size : _capacity * 2);
}
size_t end = _size + len;
while (end >= pos+len)
{
_str[end] = _str[end - len];
end--;
}
for (int i = pos,j=0; j < len;j++, i++)
{
_str[i] = str[j];
}
_size += len;
return *this;
}
string 在C++中算是比较重要的了,也是入门时有必要所学的容器。在往常中运用的频率较高,所以咱们不只要把握其简略的用法,更应该去了解其底层的完结。这有助于咱们后续的运用和了解。本篇文章列举出了string中常用的语法和接口底层的底层完结,这些都是咱们应该熟练把握的内容。