【C++】string类

小羊在奋斗 2024-08-15 09:05:01 阅读 77

头像

🚀个人主页:奋斗的小羊

🚀所属专栏:C++

很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~

目录

前言💥1、标准库中的string类💥1.1string类的常用接口💥string类对象常见构造💥string类对象容量操作💥string类对象修改操作💥string类非成员函数

💥1.2auto和范围for💥1.2.1遍历字符串的三种方式💥下标 + [ ] 重载💥迭代器(通用)💥范围for(通用)


前言

C语言中,字符串是以’\0’结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP(分装、继承、多态)的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。


💥1、标准库中的string类

💥1.1string类的常用接口

💥string类对象常见构造

函数名称 功能
<code>string() 构造空的string类对象,即空字符
string(const char* s) 用常量来构造string类对象
string(size_t n, char c) string类对象中包含n个字符c
string(const string& s) 拷贝构造函数

#include <iostream>

#include <string>

using namespace std;

int main()

{

string s1;//构造空的string类对象

string s2("123456");//用常量构造string类对象

string s3(s2);//拷贝构造

return 0;

}

operator[]:返回字符串中位置 pos 的字符引用,像数组一样修改某个位置的字符:

class string

{

public:

char& operator[](size_t i)

{

assert(i < _size);

return _str[i];

}

private:

char* _str;

size_t _size;

size_t capactiy;

};

在这里插入图片描述


💥string类对象容量操作

函数名称 功能
<code>size 返回字符串有效字符长度
length 返回字符串有效字符长度
resize 将有效字符的个数改成n个,多出的空间用字符c填充
capacity 返回空间总大小
reserve 提前开空间,避免扩容,为字符串预留空间
clear 清空有效字符,容量不变
empty 检测字符串释放为空串,是返回true,否则返回false

string类对象的空间不包含字符'/0'void reserve (size_t n = 0):请求将字符串容量调整为计划的大小更改,长度不超过 n 个字符。如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 个字符(或更大)。此函数对字符串长度没有影响,并且不能更改其内容。在VS中大了扩容,小了不变size()length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一致,一般情况下基本都是用size()clear()只是将string中有效字符清空,不改变底层空间大小resize(size_t n)resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字符个数增多时,resize(n)0来填充多出的元素空resize(size_t n, charc)用字符c来填充多出的元素空间。注意resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变


💥string类对象修改操作

函数 功能
push_back 在字符串后尾插入字符c
append 在字符串后追加一个字符串
operator+= 在字符串后追加字符串
c_str 返回c格式
find + npos 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
rfind 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
substr 在str中从pos位置开始,截取n个字符,然后将其返回

string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留


💥string类非成员函数

函数 功能
operator>> 输入运算符重载
operator<< 输出运算符重载
getline 获取一行字符

getline解决了输入操作时遇到空格、换行就结束的问题

| string类的接口测试:

#include <iostream>

using namespace std;

#include <string>

// 测试string容量相关的接口

// size/clear/resize

void Teststring1()

{

// 注意:string类对象支持直接用cin和cout进行输入和输出

string s("hello, bit!!!");

cout << s.size() << endl;

cout << s.length() << endl;

cout << s.capacity() << endl;

cout << s << endl;

// 将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小

s.clear();

cout << s.size() << endl;

cout << s.capacity() << endl;

// 将s中有效字符个数增加到10个,多出位置用'a'进行填充

// “aaaaaaaaaa”

s.resize(10, 'a');

cout << s.size() << endl;

cout << s.capacity() << endl;

// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充

// "aaaaaaaaaa\0\0\0\0\0"

// 注意此时s中有效字符个数已经增加到15个

s.resize(15);

cout << s.size() << endl;

cout << s.capacity() << endl;

cout << s << endl;

// 将s中有效字符个数缩小到5个

s.resize(5);

cout << s.size() << endl;

cout << s.capacity() << endl;

cout << s << endl;

}

//====================================================================================

void Teststring2()

{

string s;

// 测试reserve是否会改变string中有效元素个数

s.reserve(100);

cout << s.size() << endl;

cout << s.capacity() << endl;

// 测试reserve参数小于string的底层空间大小时,是否会将空间缩小

s.reserve(50);

cout << s.size() << endl;

cout << s.capacity() << endl;

}

// 利用reserve提高插入数据的效率,避免增容带来的开销

//====================================================================================

void TestPushBack()

{

string s;

size_t sz = s.capacity();

cout << "making s grow:\n";

for (int i = 0; i < 100; ++i)

{

s.push_back('c');

if (sz != s.capacity())

{

sz = s.capacity();

cout << "capacity changed: " << sz << '\n';

}

}

}

// 构建vector时,如果提前已经知道string中大概要放多少个元素,可以提前将string中空间设置好

void TestPushBackReserve()

{

string s;

s.reserve(100);

size_t sz = s.capacity();

cout << "making s grow:\n";

for (int i = 0; i < 100; ++i)

{

s.push_back('c');

if (sz != s.capacity())

{

sz = s.capacity();

cout << "capacity changed: " << sz << '\n';

}

}

}

// string的遍历

// begin()+end() for+[] 范围for

// 注意:string遍历时使用最多的还是for+下标 或者 范围for(C++11后才支持)

// begin()+end()大多数使用在需要使用STL提供的算法操作string时,比如:采用reverse逆置string

void Teststring3()

{

string s1("hello Bit");

const string s2("Hello Bit");

cout << s1 << " " << s2 << endl;

cout << s1[0] << " " << s2[0] << endl;

s1[0] = 'H';

cout << s1 << endl;

// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改

}

void Teststring4()

{

string s("hello Bit");

// 3种遍历方式:

// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,

// 另外以下三种方式对于string而言,第一种使用最多

// 1. for+operator[]

for (size_t i = 0; i < s.size(); ++i)

cout << s[i] << endl;

// 2.迭代器

string::iterator it = s.begin();

while (it != s.end())

{

cout << *it << endl;

++it;

}

// string::reverse_iterator rit = s.rbegin();

// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型

auto rit = s.rbegin();

while (rit != s.rend())

cout << *rit << endl;

// 3.范围for

for (auto ch : s)

cout << ch << endl;

}

//

// 测试string:

// 1. 插入(拼接)方式:push_back append operator+=

// 2. 正向和反向查找:find() + rfind()

// 3. 截取子串:substr()

// 4. 删除:erase

void Teststring5()

{

string str;

str.push_back(' '); // 在str后插入空格

str.append("hello"); // 在str后追加一个字符"hello"

str += 'b'; // 在str后追加一个字符'b'

str += "it"; // 在str后追加一个字符串"it"

cout << str << endl;

cout << str.c_str() << endl; // 以C语言的方式打印字符串

// 获取file的后缀

string file("string.cpp");

size_t pos = file.rfind('.');

string suffix(file.substr(pos, file.size() - pos));

cout << suffix << endl;

// npos是string里面的一个静态成员变量

// static const size_t npos = -1;

// 取出url中的域名

string url("http://www.cplusplus.com/reference/string/string/find/");

cout << url << endl;

size_t start = url.find("://");

if (start == string::npos)

{

cout << "invalid url" << endl;

return;

}

start += 3;

size_t finish = url.find('/', start);

string address = url.substr(start, finish - start);

cout << address << endl;

// 删除url的协议前缀

pos = url.find("://");

url.erase(0, pos + 3);

cout << url << endl;

}

Leetcode——仅仅反转字母Leetcode——找字符串中第一个只出现一次的字符Leetcode——字符串相加


💥1.2auto和范围for

| auto关键字

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得auto声明指针类型时,用autoauto*没有任何区别,但用auto声明引用类型时则必须加&当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量auto不能作为函数的参数,可以做返回值,但是建议谨慎使用auto不能直接用来声明数组

auto的价值是简化代码

map<string, string> dict;

//map<string, string>::iterator mit = dict.begin();

auto mit = dict.begin();

| 范围for

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束范围for可以作用到数组和容器对象上进行遍历范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到

常规遍历数组:

void test()

{

int arr[] = { 1,2,3,4,5 };

for (size_t i = 0; i < sizeof(arr) / sizeof(int); i++)

{

cout << arr[i] << " ";

}

}

范围for遍历数组:

void test()

{

int arr[] = { 1,2,3,4,5 };

for (auto n : arr)

{

cout << n << " ";

}

}


💥1.2.1遍历字符串的三种方式

💥下标 + [ ] 重载

size_t size() const:返回字符串的长度(以字节为单位)

void test()

{

string s1("abcdef");

for (size_t i = 0; i < s1.size(); i++)

{

cout << s1[i] << " ";

}

cout << endl;

}


💥迭代器(通用)

迭代器提供了一种通用的访问所有容器的方式

| 正向迭代器:

iterator begin():返回一个迭代器,该迭代器指向字符串的第一个字符iterator end():返回一个迭代器,该迭代器指向字符串的末尾字符('/0')

void test()

{

string s1("abcdef");

string::iterator it = s1.begin();

while (it != s1.end())

{

cout << *it << " ";

++it;

}

cout << endl;

}

*itoperator*运算符重载的结果迭代器可修改

请添加图片描述

| 反向迭代器:

<code>reverse_iterator rbegin():返回一个反向迭代器,指向字符串的最后一个字符(即其反向开头)reverse_iterator rend():返回一个反向迭代器,指向字符串的第一个字符(被视为其反面端)之前的理论元素反向迭代器向后迭代:增加迭代器会使它们朝向字符串的开头

void test()

{

string s("abcdef");

string::reverse_iterator rit = s.rbegin();

while (rit != s.rend())

{

cout << *rit << " ";

++rit;

}

cout << endl;

}

请添加图片描述

如果对象是常量字符串,则只可读不可写。以正向迭代器为例:

<code>void test()

{

const string s("abcdef");

string::const_iterator cit = s.begin();

while (cit != s.end())

{

cout << *cit << " ";

++cit;

}

cout << endl;

}


💥范围for(通用)

自动从容器中取每一个值给变量(ch)自动赋值,自动迭代,自动判断结束

void test()

{

string s1("abcdef");

for (auto ch : s1)

{

cout << ch << " ";

}

cout << endl;

}

范围for的底层还是迭代器,所以范围for也可修改

请添加图片描述

但当我们重新打印s1时还是原来值:

请添加图片描述

这是因为底层<code>*it的值给给ch只是拷贝,要想通过ch改变可以考虑传引用。

请添加图片描述




声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。