目录
目录
一.STL简介
1.什么是STL
2.STL的版本
3.STL的六大组件
4.STL的重要性
5.如何学习STL
6.STL的缺陷
二.STL--string的使用与模拟实现
1.string的使用
1.1了解string
1.2string常用接口
string类对象的容量操作
string类对象的访问及遍历操作
string类对象的修改操作
string类非成员函数
2.string的模拟实现
STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。
原始版本
Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意 运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使 用。 HP 版本--所有STL实现版本的始祖。
P. J. 版本
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低, 符号命名比较怪异。
RW版本
由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。
SGI版本
由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好, 可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。我们后面学习STL要阅读部分源代码, 主要参考的就是这个版本。
此外,在C++STL学习过程中,博主推荐阅读侯捷老师的《STL源码剖析》,这本书参考的就是SGI版本的STL,一些网站上也有人上传了侯捷老师STL这块的视频,如果各位能点进我的这篇博客相信找到这些视频也不在话下(doge)
几个名词可能有些陌生,但随着学习的深入自然会迎刃而解
1.刷题,笔试中经常要用
无论是平时在各大平台刷题,还是将来找工作笔试都要涉及到STL,如vector,string,map,set等等等等
2.面试官的考核内容会涉及
很现实=-=
3.工作中
世界上有两种人,一种会STL的,一种不会STL的(bushi),会了这些STL显然能提高工作效率,当然对底层逻辑的了解时必不可少的
前辈的话语,字字珠玑
总之,学习STL三重境界就是:能用,明理,能扩展
(貌似学习任何东西都有类似的层级(小声))
1.STL库的更新太慢了。这个得严重吐槽,上一版靠谱是C++98,中间的C++03基本一些修订。C++11出 来已经相隔了13年,STL才进一步更新。
2. STL现在都没有支持线程安全。并发环境下需要我们自己加锁。且锁的粒度是比较大的。
3. STL极度的追求效率,导致内部比较复杂。比如类型萃取,迭代器萃取。
4. STL的使用会有代码膨胀的问题,比如使用vector/vector/vector这样会生成多份代码,当然这是模板语 法本身导致的。
emmmm以后有水平了再来想它的缺陷吧(bushi)
1. string是表示字符串的字符串类
2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;
4. 不能操作多字节或者变长字符的序列。
在使用string类时,必须包含#include头文件以及using namespace std;
我们可以在cplusplus等网站上查询常用stl容器的接口,发现眼花缭乱,其实我们常用的只有几个,在工作中用到的时候可以再查询,掌握了常用接口的使用以及实现,对于其他接口也能轻松掌握
标准库中string中有7个接口,下面讲解和使用其中4个
default(1) |
string(); |
---|---|
copy(2) | string (const string& str); |
substring (3) | string (const string& str, size_t pos, size_t len = npos); |
from c-string (4) | string (const char* s); |
from sequence (5) | string (const char* s, size_t n); |
fill (6) | string (size_t n, char c); |
range (7) | template |
string()
Constructs an empty string, with a length of zero characters
构造一个长度为0的空字符串
#include
#include
using namespace std;
int main()
{
string s;
cout << s;
return 0;
}
string(const char* s)
Copies the null-terminated character sequence (C-string) pointed by s.
赋值s指向的以空字符结尾的C字符序列
string(size_t n, char c)
Fills the string with n consecutive copies of character c
用字符c的n个连续副本填充字符串
string(const string&s)
Constructs a copy of str.
拷贝构造
size_t size() const;
Return length of string
返回字符串长度
Returns the length of the string, in terms of bytes.
This is the number of actual bytes that conform the contents of the string, which is not necessarily equal to its storage capacity.
这是构成字符串的实际字节数,这不一定等于容量
Note that string objects handle bytes without knowledge of the encoding that may eventually be used to encode the characters it contains. Therefore, the value returned may not correspond to the actual number of encoded characters in sequences of multi-byte or variable-length characters (such as UTF-8).
注意string对象处理字节时不知道可能用于编码对象的编码。因此返回值不一定等同于多字节或者可变长字符序列(如 UTF-8)
这里的更深层的理解就是:我们都是熟知ASCLL表,我们打印的字符,或者符号都是被数字所表示的。因为计算机是只认识二进制,所以这些字符或者符号都会变成二进制让计算机识别。世界上有各种语言,所以对应的就有不同的电脑编码。英文对应的电脑编码是ASCLL,中文对应的电脑编码很多时候就是统一码
统一码(Unicode),也叫万国码、单一码,由统一码联盟开发,是计算机科学领域里的一项业界标准,包括字符集、编码方案等。统一码是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。
统一码是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。统一码用数字0-0x10FFFF来映射这些字符,最多可以容纳1114112个字符,或者说有1114112个码位。码位就是可以分配给字符的数字。UTF-8、UTF-16、UTF-32都是将数字转换到程序数据的编码方案。分别映射为8位,16位,32位长的整数
size_t length() const;
Return length of string
返回字符串长度
length与size的实现相同,只是命名不一样,这个是历史遗留问题,就不用过多的吐槽,在后面容器中就再也没有length了,只在string中有。
size_t capacity() const;
Return size of allocated storage
返回分配容量的大小
bool empty() const;
Test if string is empty
空true 非空false
检验字符串是否为空
void clear();
Clear string
清空字符串
注意
为了提高重复利用效率,只清空有效字符并不清理内存
reserve
void reserve (size_t n = 0);
Request a change in capacity
请求改变容量
vs2019下以1.5倍增容
linux下2倍增容
void resize (size_t n);
void resize (size_t n, char c);
Resize string
调整字符串大小
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
Returns a reference to the character at position pos in the string.
返回pos位置的字符
iterator begin();
Returns an iterator pointing to the first character of the string
返回指向字符串第一个字符的迭代器
const_iterator begin() const;
Returns an iterator pointing to the past-the-end character of the string.
返回一个指向字符串的结束字符的迭代器
reverse_iterator rbegin();
Return reverse iterator to reverse beginning
返回以反向开始的反向迭代器
const_reverse_iterator rbegin() const;
Return reverse iterator to reverse end
返回反向端的反向迭代器
void push_back (char c);
appends character c to the end of the string, increasing its length by one.
将字符c追加到字符串的末尾,将其长度增加1
string (1) | string& append (const string& str); |
|
---|---|---|
substring (2) | string& append (const string& str, size_t subpos, size_t sublen); |
|
c-string (3) | string& append (const char* s); |
|
buffer (4) |
|
|
fill (5) | string& append (size_t n, char c); |
|
range (6) | template |
(1) string
Appends a copy of str.----追加str的副本
(2) substring
Appends a copy of a substring of str. The substring is the portion of str that begins at the character position subpos and spans sublen characters (or until the end of str, if either str is too short or if sublen is string::npos).
追加str的子字符串的副本。该子字符串是str的一部分,从字符位置子字符串开始,跨越子字符串字符(或直到str结尾,如果str太短或子字符串为string::npos)
(3) c-string
Appends a copy of the string formed by the null-terminated character sequence (C-string) pointed by s.
追加由s指向的以空结尾的字符序列(C字符串)形成的字符串的副本
(4) buffer
Appends a copy of the first n characters in the array of characters pointed by s.
追加由s指向的字符数组中前n个字符的副本
(5) fill
Appends n consecutive copies of character c.
追加n个字符c的连续副本。
(6) range
Appends a copy of the sequence of characters in the range [first,last), in the same order.
以相同的顺序追加范围[first,last]中字符序列的副本。
operator+=
Append to string
附加到字符串
string (1) | string& operator+= (const string& str); |
|
---|---|---|
c-string (2) |
|
|
character (3) | string& operator+= (char c); |
const char* c_str() const;
Get C string equivalent
获取等效的C字符串
find + npos
static const size_t npos = -1;(四十多亿的一个数值)
string (1) |
|
|
---|---|---|
c-string (2) | size_t find (const char* s, size_t pos = 0) const; |
|
buffer (3) | size_t find (const char* s, size_t pos, size_t n) const; |
|
character (4) | size_t find (char c, size_t pos = 0) const; |
rfind
string (1) | size_t rfind (const string& str, size_t pos = npos) const; |
---|---|
c-string (2) | size_t rfind (const char* s, size_t pos = npos) const; |
buffer (3) | ize_t rfind (const char* s, size_t pos, size_t n) const; |
character (4) | size_t rfind (char c, size_t pos = npos) const; |
string substr (size_t pos = 0, size_t len = npos) const;
(leetcod刷题的时候巨好用)
Returns a newly constructed string object with its value initialized to a copy of a substring of this object
返回一个新构造的字符串对象,其值初始化为此对象的子字符串的副本。
注意:
1. 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好
|
string operator+ (const string& lhs, const string& rhs); |
|
---|---|---|
c-string (2) | string operator+ (const string& lhs, const char* rhs); string operator+ (const char* lhs, const string& rhs); |
|
character (3) | string operator+ (const string& lhs, char rhs); string operator+ (char lhs, const string& rhs); |
ostream& operator<< (ostream& os, const string& str);
(1) | istream& getline (istream& is, string& str, char delim); |
---|---|
(2) | istream& getline (istream& is, string& str); |
relational operators (string)
(1) | bool operator== (const string& lhs, const string& rhs); bool operator== (const char* lhs, const string& rhs); bool operator== (const string& lhs, const char* rhs); |
---|---|
(2) | bool operator!= (const string& lhs, const string& rhs); bool operator!= (const char* lhs, const string& rhs); bool operator!= (const string& lhs, const char* rhs); |
(3) | bool operator< (const string& lhs, const string& rhs); bool operator< (const char* lhs, const string& rhs); bool operator< (const string& lhs, const char* rhs); |
(4) | bool operator<= (const string& lhs, const string& rhs); bool operator<= (const char* lhs, const string& rhs); bool operator<= (const string& lhs, const char* rhs); |
(5) | bool operator> (const string& lhs, const string& rhs); bool operator> (const char* lhs, const string& rhs); bool operator> (const string& lhs, const char* rhs); |
(6) | bool operator>= (const string& lhs, const string& rhs); bool operator>= (const char* lhs, const string& rhs); bool operator>= (const string& lhs, const char* rhs); |
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
friend istream& operator>>(istream& in, const string& s);
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
void push_back(char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
/*
char* newstr = new char[newcapacity + 1];
strcpy(newstr , _str);
delete[] _str;
_str = newstr;
_capacity = newcapacity;
*/
reserve(newcapacity + 1);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
size_t newcapacity = _size + len;
/*
char* newstr = new char[newcapacity + 1];
strcpy(newstr , _str);
delete[] _str;
_str = newstr;
_capacity = newcapacity;
*/
reserve(newcapacity + 1);
}
strcpy(_str + _size, str);
_size += len;
}
void reserve(size_t n)
{
if (n > _capacity)
{
char* newstr = new char[n];
strcpy(newstr, _str);
delete[] _str;
_str = newstr;
_capacity = n;
}
}
void resize(size_t n, char ch = '\0')
{
if (n >= _size)
{
if (n > _capacity)
{
reserve(n);
}
for (size_t idx = _size; idx < n; ++idx)
{
_str[idx] = ch;
}
}
_str[_size = n] = '\0';
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
string(const string& s) :_str(new char[s._capacity]), _size(s._size), _capacity(s._capacity)
{
strcpy(_str, s._str);
}
char& operator[](const size_t idx)
{
assert(idx < _size);
return _str[idx];
}
const char& operator[](const size_t idx)const
{
assert(idx < _size);
return _str[idx];
}
bool operator ==(const string& s)
{
return strcmp(_str, s._str) == 0;
}
bool operator <(const string& s)
{
return strcmp(_str, s._str) < 0;
}
bool operator >(const string& s)
{
return !(*this <= s);
}
bool operator <=(const string& s)
{
return *this == s || *this < s;
}
bool operator >=(const string& s)
{
return !(*this < s);
}
bool operator !=(const string& s)
{
return !(*this == s);
}
string& operator=(const string& s)
{
if (this != &s)
{
char* tmp = new char[s.capacity()];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
const char* c_str()const
{
return _str;
}
~string()
{
delete[] _str;
_size = _capacity = 0;
_str = nullptr;
}
string& insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity + 1);
}
size_t end = _size;
while (end > pos)
{
_str[end] = _str[end - 1];
end--;
}
_str[end] = ch;
++_size;
_str[_size] = '\0';
return *this;
}
string& insert(size_t pos, const char* str)
{
assert(pos < _size);
size_t len = strlen(str);
if (_size + len >= _capacity)
{
size_t newcapacity = _size + len;
reserve(newcapacity + 1);
}
size_t end = _size - 1 + len;
while (end - len > pos)
{
_str[end] = _str[end - len];
end--;
}
_str[end] = _str[end - len];
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
if (len > _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t idx = pos;
while (idx + len < _size)
{
_str[idx] = _str[idx + len];
idx++;
}
_str[idx] = '\0';
_size -= len;
}
}
size_t find(const char* str, size_t pos = 0)
{
char* p = strstr(_str, str);
if (p)
{
return p - _str;
}
return npos;
}
void clear()
{
_str[_size = 0] = '\0';
}
bool empty()
{
return _size == 0;
}
private:
char* _str;
size_t _size;
size_t _capacity;
static size_t npos;
}
;
size_t string::npos = -1;
ostream& operator<<(ostream& out, const string& s)
{
for (size_t i = 0; i < s.size();
++i)
out << s[i];
return out;
}
istream& operator>>(istream& in, const string& s)
{
in >> s._str;
return in;
}