1. 标准库中的string类
1.1 string类(了解)
string - C++ Reference
在使用string类时,必须包含 # include头文件以及 using namespace std;
1.2 auto和范围for
1)auto关键字
- 作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得
- 用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
- 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
- auto不能作为函数的参数,可以做返回值,但是建议谨慎使用
- auto不能直接用来声明数组
2)范围for
- for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束
- 范围for可以作用到数组和容器对象上进行遍历
- 范围for的底层很简单,容器遍历实际就是替换为迭代器,从汇编层可以看到
1.3 string类的常用接口说明
1)string类对象的常见构造
string::string - C++ Reference
string::size - C++ Reference 函数名称 | 功能说明 |
string() (重点) | 构造空的string类对象,即空字符串 |
string(const char* s) (重点) | 用C-string来构造string类对象 |
string(size_t n, char c) | 用n个连续字符c填充string类对象 |
string(const string&s) (重点) | 拷贝构造函数 |
string s1;//构造空的string类对象s1
string s2("hello world");
string s3(s2);
2) string类对象的容量操作
函数名称 | 功能说明 |
size(重点) | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty (重点) | 检测字符串释放为空串,是返回true,否则返回false |
clear (重点) | 清空有效字符 |
reserve (重点) | 为字符串预留空间 |
resize (重点) | 将有效字符的个数该成n个,多出的空间用字符c填充 |
length | 返回字符串有效字符长度 |
注意:
- string类对象支持直接用cin和cout进行输入和输出
- size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()
- clear()只是将string中有效字符清空,不改变底层空间大小
- resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的元素空间。注意:如果是大于原空间容量(n>=capacity),将元素有效个数增多,可能会改变底层容量的大小(默认插入'\0'),如果是小于原空间容量(n<capacity),将元素有效个数删除,底层空间总大小不变
- reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于string的底层空间总大小时,reserver不会改变容量大小
string s("hello, bit!!!");
// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
s.clear();
// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
s.resize(10, 'a');
// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
// 注意此时s中有效字符个数已经增加到15个
s.resize(15);
// 将s中有效字符个数缩小到5个
s.resize(5);
3)string类对象的访问及遍历操作
函数名称 | 功能说明 |
operator[](重点) | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器 |
rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位 置的迭代器(反向迭代器) |
范围for | C++11支持更简洁的范围for的新遍历方式 |
// 3种遍历方式:
// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
string s("hello Bit");
// 1. 下标+[]
for (size_t i = 0; i < s.size(); ++i)
{
cout<<s[i]<<endl;
}
//2.迭代器
string::iterator it = s.begin();
//auto it = s.begin();
while (it != s.end())
{
cout << *it << endl;
++it;
}
// string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,推导迭代器的类型
auto rit = s.rbegin();
//string::reverse_iterator rit = s.rbegin();
while (rit!=s.rend())
{
cout << *rit << endl;
++rit;
}
// 3.范围for
//自动取容器数据赋值给左边对象
//自动++,自动判断结束
//底层是迭代器
for (auto ch : s)
{
cout << ch << " ";
}
cout << endl;
//修改s中数据
for (auto& ch : s)
{
ch++;
}
for (auto& ch : s)
{
cout << ch << " ";
}
cout << endl;
4)string类对象的修改操作
函数名称 | 功能说明 |
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
operator+=(重点) | 点) 在字符串后追加字符串str |
c_str(重点) | 返回C格式字符串 |
find+npos(重点) | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的 位置 |
rfind | 同上 |
substr | 在str中从pos位置开始,截取len个字符,然后将其返回 |
erase | 在str中从pos位置开始,删除len个字符,然后将其返回 |
注意:
- 在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差 不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可 以连接字符串
- 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好
5) string类非成员函数
函数 | 功能说明 |
getline(重点) | 从is中提取字符并将其存储到str中,直到找到分隔符delim(或换行符'\n') |
operator+ | 返回新字符串,因为传值返回,导致深拷贝效率低(尽量少用) |
operator>>(重点) | 输入运算符重载 |
operator<<(重点) | 输出运算符重载 |
relational operators(重点) |
大小比较 |
经典例题
仅仅反转字母
找字符串中第一个只出现一次的字符
字符串里面最后一个单词的长度
验证字符串是否回文
字符串相加
字符串部分区间反转
反转字符串中单词
字符串相乘
2.string类模拟实现
最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析构函数
注:strlen函数在计算字符串长度时不包括\0,所以开空间的时候要多开一个
2.1 模拟实现
string实现
2.2 深拷贝
1)传统版写法的String类
string::string(const char* str)
:_size(strlen(str))
{
//_str = new char[_size];多分配一个空间给\0
//_str = str;类型不同不能直接赋值
_str = new char[_size + 1];
strcpy(_str, str);
_capacity = _size ;
}
string::string(const string& str)
{
_str = new char[str._size + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
string& string::operator=(const string& str)
{
//if (_str != str._str)
if(this != &str)
{
delete[] _str;
_str = new char[str._size + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
}
return *this;
}
string::~string()
{
delete[] _str;
_str = nullptr;//漏了这一步
_capacity = 0;
_size = 0;
}
2)string中swap函数
3)现代版写法的String类
string::string(const char* str)
{
if (str == nullptr)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
void string::swap(string& str)
{
std::swap(this->_str, str._str);
std::swap(this->_size, str._size);
std::swap(this->_capacity, str._capacity);
}
string::string(const string& str)
{
string tmp(str._str);
string::swap(tmp);
}
string& string::operator=(const string& str)
{
if (this != &str)
{
string tmp(str._str);
string::swap(tmp);
}
return *this;
}
string::~string()
{
delete[] _str;
_str = nullptr;
_capacity = 0;
_size = 0;
}
2.3 写时拷贝
是在浅拷贝的基础之上增加了引用计数的方式来实现的
引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该 资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源, 如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有 其他对象在使用该资源