1.STL简介
STL(standard template library标准模版库),是c++标准库的重要组成部分,是一个包罗数据结构与算法的软件框架。
STL有很多版本,我们学习STL要阅读部分源代码,主要参考SGI版本。
STL的六大组件:
网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层 的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。
2.string类
string属于c++标准库,诞生比STL早,在广义的角度,string也属于容器。
在string内种有一百多种函数,我们只需要掌握重要的就行了。
学习这部分知识我们需要学会查字典
https://cplusplus.com
使用string类要包头文件#inlucde<string>,还有using namespace std;
constructor(构造):
string s1;//空的
string s2("hello world");
string s3(s2);
cout << s1 << endl;//
cout << s2 << endl;//hello world
cout << s3 << endl;//hello world
//string类中有流插入和流提取的函数
第三个是在给定的类中的第pos位置,开始复制npos数个。
string s4(s2, 6, 5);//world
//如果len的长度大于后面字符的长度,只复制到字符串结束
第五个是给定的字符串,复制n个。
string s6("hello world", 5);//hello
第六个是给类构造n个字符、
string s7(10, 'c');
cout << s7<< endl;//cccccccccc
这个函数的目的就是能修改对象的字符串中的字符。
s6[0] = 'x';
cout << s6;//xello
三种方法遍历string类的对象:
//下标+[]
string s("hello world");
for(size_t i=0;i<s.size();i++)
{
cout<<s[i];
}
cout<<endl;
//迭代器
string::iterator it=s.begin();//iterator像指针的东西
while(it!=s.end())//s.end()指向最后一个位置的下一个位置
{
//使用迭代器也可修改
//*it+=2;
cout<<*it;//*是运算符重载
it++;
}
//任何容器都可以用迭代器来访问
//范围for 底层就是迭代器
for(auto ch:s);//auto在c++中是自动推导。自动赋值,自动迭代,自动判断
{
ch+=2;//打印后是每个字符加2的结果,但s没变,还是原来的字符。
//要想变的话,在auto后面加&
cout<<ch;
}
cout<<endl;
auto:
这不是string里面的用法,这里来介绍一下auto的用法.
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际 只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
- auto不能直接用来声明数组
int func()
{
return 10;
}
int main()
{
int a = 10;
auto b=a;
auto c = 'a';
auto d = func();
//"auto"的符号必须具有初始值设定项
//auto e;
cout << typeid(a).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
//范围for适用于容器和数组
int array[] = { 1,2,3,4,5 };
//C++98的遍历
for (int i = 0; i < sizeof(array) / sizeof(int); i++)
{
array[i] *= 2;
}
for (int i = 0; i < sizeof(array) / sizeof(int); i++)
{
cout << array[i] << endl;
}
//C++
for (auto& e : array)
e *= 2;
for (auto& e : array)
cout << e << " " << endl;
}
auto可以缩短代码,比如:
map<string, string> dict;
//map<string, string>::iterator mit = dict.begin();
auto mit = dict.begin();
iterator:
begin和end在上面已经见识到了,那我们来看看rbegin,rend。
//反向迭代器
string::reverse_iterator rit = s1.rbegin();//rbegin指向最后一个字符
while (rit != s1.rend())//rend指向第一个字符的前一个
{
cout << *rit << " ";//d l r o w o l l e h
++rit;
}
cout << endl;
反过来遍历。
看到下面还有const_iterator begin() const,这里是让对象只可读不可改,所以我们迭代器有四种类型。
const string s2("hello world");
string::const_iterator cit = s2.begin();
while (cit != s2.end())
{
cout << *cit << " ";
++cit;
}
string::const_reverse_iterator crit = s2.rbegin();
//auto crit = s2.rbegin();
while (crit != s2.rend())
{
cout << *crit << " ";
++crit;
}
cbegin,cend返回的是被const修饰的迭代器,指向字符串,不能修改。只是与对象被const的作区分。
capacity:
size()与length()的效果一样,但length还是有局限性。一般都用size()。
max_size()是string对象所能达到的最大的长度。
resize()是改变对象的长度。对象的容量不变。
第二个用法就是长度变长后,字符c填充到新的空间。
string str("I like to code in C");
cout << str << '\n';
unsigned sz = str.size();
str.resize(sz + 2, '+');
cout << str << '\n';//I like to code in C++
str.resize(14);
cout << str << '\n';//I like to code
capacity(),容量,在vs上长度小于等于15时,容量为15。这是因为string底层有一个数组buf[16],最后一个存\0,如果数据长度小于15,存在buf上,不存在str上,大于15时,存在str开辟的堆空间上,buf就不存数据。如果在对象后面追加字符,容量会自动扩容。
class string
{
private:
char buf[16];
char*_str;
int _size;
int _capacity;
};
reserve(),是给容量开好空间(可以提前开),但空间大小大于等于给的数据。假如开的过多,容量不会缩容。假如开的不够,容量会自动扩。
clear(),清除数据,但不清除容量。
empty()是来判断字符串是否为常量。
shrink_to_fit(),是用来缩容到合适的大小,避免空间浪费。
在vs上调试能看到这些东西:
Modifiers:
operator+=,append,push_back都是尾插,一般用的最多的是+=。
push_back只能尾插字符
int main()
{
string s("hello world");
s.push_back(' ');
s.push_back('#');
cout<<s<<endl;//hello world #
return 0;
}
append可以尾插字符串,这些如果要用到,查一下就好了。
int main()
{
string s("hello world");
s.push_back(' ');
s.push_back('#');
cout<<s<<endl;//hello world #
s.append("xyz");
cout<<s<<endl;//hello world #xyz
return 0;
}
int main()
{
string s("hello world");
s.push_back(' ');
s.push_back('#');
cout<<s<<endl;//hello world #
s.append("xyz");
cout<<s<<endl;//hello world #xyz
s+='$';
cout<<s<<endl;//hello world #xyz$
return 0;
}
insert插入,在指定位置插入字符或字符串
erase删除,删除一部分字符串,来减小长度。
string s("hello world");
s.erase(0, 1);//头删
cout << s << endl;//ello world
s.erase(s.begin());llo world
cout << s << endl;
//尾删
s.erase(--s.end());
cout << s << endl;
s.erase(s.size() - 1, 1);
cout << s << endl;
string ss("hello world");
ss.erase(6);//hello
replace,将字符串的字符替换成字符或字符串。
string sss("hello world hello world");
size_t pos = sss.find(' ');
while (pos != string::npos)
{
sss.replace(pos, 1, "%%");
pos = sss.find(' ',pos+2);
}
cout << sss << endl;//hello%%world%%hello%%world
swap(),交换两个对象的内容。
String operations:
c_str(),就是获得对象的_str(字符串)。
find,就是找字符或字符串,返回下标,rfind就是倒着找。
substr就是生成一个子字符串
string s("test.cpp.zip");
size_t pos = s.rfind('.');
string suffix = s.substr(pos);
cout << suffix << endl;//.zip
Member constants:
npos:
当在字符串的成员函数中用作 len(或 sublen)参数的值时,此值表示“直到字符串的末尾”。
作为返回值,它通常用于指示不匹配。
此常量使用值 -1 定义,由于size_t是无符号整数类型,因此它是此类型的最大可能可表示值。
getline:
第一种是,输入字符,直到遇到字符delim为止,第二种是'\n'表示终止,可以识别空格。
3.模拟实现string底层
string.h:
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
using namespace std;
namespace byh
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str+_size;
}
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str+_size;
}
/*string()
:_str(new char[1] {'\0'})
, _size(0)
, _capacity(0)
{}*/
//短小频繁调用的函数,可以直接定义到类里面,默认是inline
string(const char* str="")
{
_size = strlen(str);
//_capacity不包含\0
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//深拷贝问题
string(const string& s)
{
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
string& operator=(const string& s)
{
if(this!=&s)
{
delete[]_str;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
~string()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
const char* c_str()const
{
return _str;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
void clear()
{
_str[0] = '\0';
_size = 0;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
void reserve(size_t n);
void insert(size_t pos, char ch);
void insert(size_t pos, const char* str);
void erase(size_t pos, size_t len=npos);
size_t find(char ch, size_t pos = 0);
size_t find(const char* str, size_t pos=0);
string substr(size_t pos = 0, size_t len = npos);
private:
char* _str;
size_t _size;
size_t _capacity;
static const size_t npos;
//static const size_t npos=-1;这样也行
};
bool operator<(const string& s1, const string& s2);
bool operator<=(const string& s1, const string& s2);
bool operator>(const string& s1, const string& s2);
bool operator>=(const string& s1, const string& s2);
bool operator==(const string& s1, const string& s2);
bool operator!=(const string& s1, const string& s2);
ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
}
string.cpp:
#include"string.h"
namespace byh
{
const size_t string::npos = -1;
void string::reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
++_size;
_str[_size] = '\0';
}
void string::append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
//大于二倍,需要多少开多少
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
strcpy(_str + _size, str);
_size += len;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
void string:: insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
//挪动数据
/* int end = _size;
while (end>=(int)pos)
{
_str[end + 1] = _str[end];
--end;
}
_str[pos] = ch;
_size++;*/
size_t end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
_str[pos] = ch;
_size++;
}
void string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (len == 0)
return;
if (_size + len > _capacity)
{
reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);
}
size_t end = _size + len;
while(end>pos+len-1)
{
_str[end] = _str[end-len];
end--;
}
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = str[i];
}
_size += len;
}
void string::erase(size_t pos, size_t len)
{
assert(pos < _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
for (size_t i = pos + len; i <= _size; i++)
{
_str[i - len] = _str[i];
}
_size -= len;
}
}
size_t string::find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* str, size_t pos)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr == nullptr)
{
return npos;
}
else
{
return ptr - _str;
}
}
string string::substr(size_t pos, size_t len)
{
assert(pos < _size);
//len大于剩余字符长度,更新一下len
if (len > _size - pos)
{
len = _size - pos;
}
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++)
{
sub += _str[pos + i];
}
return sub;
}
bool operator<(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator<=(const string& s1, const string& s2)
{
return s1 < s2 || s1 == s2;
}
bool operator>(const string& s1, const string& s2)
{
return !(s1 <= s2);
}
bool operator>=(const string& s1, const string& s2)
{
return !(s1 < s2);
}
bool operator==(const string& s1, const string& s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator!=(const string& s1, const string& s2)
{
return !(s1 == s2);
}
ostream& operator<<(ostream& out, const string& s)
{
for (auto ch : s)
{
out << ch;
}
return out;
}
istream& operator>>(istream& in, string& s)
{
s.clear();
const int N = 256;
char buff[N];
int i = 0;
char ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
buff[i++] = ch;
if (i == N - 1)
{
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
}
test.cpp:
#include"string.h"
using namespace std;
namespace byh
{
void test_string1()
{
string s1;
string s2("hello world");
cout << s1.c_str() << endl;
cout << s2.c_str() << endl;
for (size_t i = 0; i < s2.size(); i++)
{
s2[i] += 2;
}
cout << s2.c_str() << endl;
for (auto ch : s2)
{
cout << ch;
}
cout << endl;
string::iterator it = s2.begin();
while (it != s2.end())
{
//*it += 2;
cout << *it << " ";
++it;
}
cout << endl;
}
void test_string2()
{
string s1("hello world");
s1 += 'x';
s1 += '#';
cout << s1.c_str() << endl;
s1 += "ye";
cout << s1.c_str() << endl;
s1.insert(0, '$');
cout << s1.c_str() << endl;
s1.insert(6, "$$$");
cout << s1.c_str() << endl;
}
void test_string3()
{
string s1("hello world");
s1.erase(6, 100);
cout << s1.c_str() << endl;
string s2("hello world");
s2.erase(6);
cout << s2.c_str() << endl;
string s3("hello world");
s3.erase(6, 3);
cout << s3.c_str() << endl;
}
void test_string4()
{
string s1("test.cpp.zip");
size_t pos = s1.find('.');
string suffix = s1.substr(pos);
cout << suffix.c_str() << endl;
}
void test_string5()
{
string s1("hello world");
string s2("hello world");
cout << (s1 < s2) << endl;
cout << (s1 == s2) << endl;
cout << ("hello world" == s2) << endl;
cout << (s1 > "hello") << endl;
//cout<<("hello"=="hello")<<endl;
cout << s1 << s2 << endl;
string s0;
cin >> s0;
cout << s0 << endl;
}
}
int main()
{
//byh::test_string1();
byh::test_string5();
return 0;
}
完,感谢观看。