目录

前言
c++大大增加了对字符串的支持,除了使用c风格的字符串,还有内置的steing类。string类是c++里STL容器中的一个重要组成部分,在OJ中,和字符串有关的题目通常都以string类的形式出现。而且为了简单方便,在常规工作中都使用string类,很少有人使用c库中的字符串函数。那么接下来,我将带大家来了解一下string类的相关知识。
提示:以下是本篇文章正文内容,下面案例可供参考
一、标准库中的string类
1.string类对象的常见构造
string类有七个构造函数,下面我将挑几个常见的进行演示,如果有小伙伴想对其它进行详细了解,可以进入网址string::string - C++ Reference (cplusplus.com)。
代码如下:
int main()
{
string s1; //构造空的string类对象
cout << s1 << endl;
string s2("hello bit"); //用c字符串构造
cout << s2 << endl;
string s3(s2); //拷贝构造
cout << s3 << endl;
string s4 = "hello bit"; //这里发生运算符重载,相当于拷贝构造
cout << s4 << endl;
string s5(s4, 3, 4);//string (const string& str, size_t pos, size_t len);
cout << s5 << endl; // 从pos开始,后面的len个字符构造
const char* url = "http://www.cplusplus.com/reference/string/string/string/";
string s6(url, 4); //用字符串的前n个字符构造string类
cout << s6 << endl;
string s7(10, 'x'); //用10个x字符构造
cout << s7 << endl;
s7 = s2; //运算符重载
cout << s7 << endl;
return 0;
}
打印结果如下:
2、string类的遍历
2.1.下标+[]
int main()
{
string s2("hello bit");
for (size_t i = 0; i < s2.size(); ++i)
{
// s2.operator[](i)
s2[i] = 'x';
}
cout << endl;
for (size_t i = 0; i < s2.size(); ++i)
{
// s2.operator[](i)
cout << s2[i] << " ";
//cout << s2.at(i) << " ";
}
cout << endl;
}
size()功能:返回字符串有效字符长度。打印结果如下:
2.2迭代器遍历
我们在这里先简单展示迭代器的使用,在之后的文章中再详细介绍迭代器的原理。
// [begin(), end() ) end()返回的不是最后一个数据位置的迭代器,返回是最后一个位置下一个位置
// 也要注意的是,C++中凡是给迭代器一般都是给的[)左闭右开的区间
// 迭代器是类似指针一样东西,具体是什么我们讲了底层实现才能知道
int main()
{
string s2("hello bit");
string::iterator it=s2.begin();
while(it!=s2.end)
{
cout<<*it<<" ";
++it;
}
cout<<endl;
//反向迭代器,反向打印
string::reverse_iterator rit = s2.rbegin();
while (rit != s2.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
}
打印结果:
2.3范围for遍历
int main()
{
string s3("123456");
for(auto& e:s3)
{
e+=1;
}
for(auto e:s3)
{
cout<<e;
}
cout<<endl;
return 0;
}
打印结果如下:
3、string类的增删查改
3.1增加
int main()
{
string s1;
//尾插字符
s1.push_back('h');
s1.push_back('e');
s1.push_back('l');
s1.push_back('l');
s1.push_back('o');
//尾插字符串
s1.append("world");
cout << s1 << endl;
string s2("!!!!");
s1.append(s2);
//等价于s1.append(s2.begin(), s2.end());
cout << s1 << endl;
//实际最喜欢这样用
s1 += s2;//等价于s1+="!!!!";
cout << s1 << endl;
// 尽量少用insert,因为底层实现是数组,头部或者中间插入需要挪动数据
s1.insert(1, s2); //等价于s1.insert(1, "!!!!");
cout << s1 << endl;
}
打印结果:
、、
3.2删除
int main()
{
string s1("hello")
s1.erase(0, 1);//从0位置开始删除1个字符
cout << s1 << endl;
s1.erase(1, 10);//要删除的字符个数大于剩余字符个数不会报错,会把1之后的所有字符删完
cout << s1 << endl;
/*s1.erase(3, 10);*/ //这样写会报错
return 0;
}
3.3查找
find从pos开始向后查找,可以用来查找字符或字符串,默认返回第一个字符的位置。refind是从后向前查找。
substr用来取出原字符串中的一段字符串。从pos开始,取出后面len长度的字符串。len大于字符串长度会取出后面的所有字符。substr和find结合可以有很多用处,例如取出一个网址的域名。
//用来取出一个网址的域名
string GetDomain(const string& url)
{
size_t pos = url.find("://");
if (pos != string::npos)
{
size_t start = pos + 3;
size_t end = url.find('/', start);
if (end != string::npos)
{
return url.substr(start, end - start);
}
else
{
return string();
}
}
else
{
return string();
}
}
int main()
{
string s1("hello world");
cout << s1 << endl; // 调用 operator<<(cout, s1)
cout << s1.c_str() << endl; // 调用 operator<<(cout, const char*)
s1.resize(20);
cout << strlen(s1.c_str()) << endl;//这样写可以算出有效字符的个数
cout << s1.size() << endl << endl;
// 要求写一个程序分别取出域名和协议名
string url1 = "http://www.cplusplus.com/reference/string/string/rfind/";
cout << GetDomain(url1) << endl;
return 0;
}
3.4 修改
string的修改比较简单,string类中对[ ]运算符进行了重载,我们可以像数组一样一样直接使用[ ]加下表的方式对string值进行修改。
int main()
{
string s1="abcde";
s1[2]='a';
}
4.string类其他常见接口说明
在Capacity部分,主要有如下函数, 下面我将挑几个重要的说明。
4.1size和capacity
看如下代码:
int main()
{
string s1("hello world");
cout << s1 << endl;
cout << s1.size() << endl; //求s1中字符的个数
cout << s1.capacity() << endl; //求s1的容量
s2.clear(); //清空s1中的字符,但是不改变容量大小
cout << endl;
cout << s1.size() << endl;
cout << s1.capacity() << endl;
return 0;
}
打印结果如下:
4.2 resize和reserve
resize可以给string开辟特定的空间,并将其中的值初始化,默认的初始化值为/0
int main()
{
string s1;
s1.resize(20, 'x'); //如果写成s1.resize(20);默认的初始化值为/0
cout << "size:" << s1.size() << endl;
cout << "capacity:" << s1.capacity() << endl;
cout << s1 << endl;
}
如果原本string中有值则在后面追加。
int main
{
string s2("hello world");
s2.resize(20, 'x');
cout << s2 << endl;
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl;
cout << endl;
s2.resize(5); //输入的值小于原来的值,会保留前5位,删除后面的值
cout << s2 << endl;
cout << "size:" << s2.size() << endl;
cout << "capacity:" << s2.capacity() << endl << endl;
}
reserve和revize一样是用来开辟空间的,但区别是reserve不会对开辟的空间进行初始化,如果尾插的话会从第一个位置开始向后添加数据。
二、string模拟实现
学习STL,我们不仅要做到熟练使用它,还要对其中一些重要内容的实现原理有一定了解。相信通过第一部分的介绍,大家对于string类中一些常见接口已经有了一定了解,那么下面就让我们对这些常见接口模拟实现一下吧,这将使我们对string类有更深刻的理解。下面重点介绍如何实现string类的构造、拷贝构造、赋值运算符重载以及析构函数。
注意:如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
1.传统版写法的string类
class string
{
public:
//构造函数
string(const char* str = " ")
{
if (nullptr == str)
{
str = " ";
}
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//拷贝构造
string(const string& s)
: _str(new char[strlen(s._str) + 1])
,_size(s._size)
,_capacity(s._capacity)
{
strcpy(_str, s._str);
}
//赋值运算符重载
string& operator=(const string& s)
{
if (this != &s)
{
char* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
_size = s._size;
_capacity=s._capacity;
}
return *this;
}
//析构函数
~string()
{
if (_str)
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
}
private:
char* _str;
size_t _size;
size_t _capacity;
}
2.现代版写法的string类
class string
{
public:
//构造函数
string(const char* str = " ")
{
if (nullptr == str)
{
str = " ";
}
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//交换函数重载
void swap(string& s)
{
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//拷贝构造现代写法
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
string temp(s._str);
swap(temp);
}
//赋值运算符现代写法
string& operator=(string s)
{
swap(s);
return *this;
}
//析构函数
~string()
{
if (_str)
{
delete[] _str;
_str = nullptr;
_size = 0;
_capacity = 0;
}
}
private:
char* _str;
size_t _size;
size_t _capacity;
}
以上主要展示了string类的构造,拷贝,析构,赋值等函数的模拟实现,如果小伙伴们想了解更详细的内容,欢迎进入我的gitee仓库,里面有更加全面的模拟实现string模拟实现 · 15188705658/c++ - 码云 - 开源中国 (gitee.com)
总结
本文主要介绍了string类的一些常见接口和部分内容的模拟实现,希望大家能通过本文对string类有更加深入的了解,STL的内容还有很多,后面我将继续介绍。如果感觉本篇本章有帮助的话不妨点个赞支持一下博主哦~我也将努力带来更多干货内容,感谢阅读,你的支持就是对我最大的鼓励。