【C++语言】想学STL,先细细拿捏string类,万字详解string类 (内附精美思维导图)

简介: 【C++语言】想学STL,先细细拿捏string类,万字详解string类 (内附精美思维导图)

✨思维导图附上

看不清楚戳这里【String思维导图】

image.png

图片因为上传上限是5MB,所以不能直接传。图片无法更新但网页持续更新。


这一节我们将详细学习string字符串这个类,string类是早于STL标准库的,后面把string归属到STL,支持标准库中很多操作。触类旁通,通过学习string,也便于我们更好的学习STL。活不多说开始~


string 字符串

我们学习关于STL的学习通过【cplusplus旧网站】来学习。

1.string类简介

我就充当翻译官吧,不对的地方欢迎指正:

  • String 类是一个字符序列的对象。
  • 标准字符串类提供了类似于标准字节容器的接口,但增加了专门用于操作单字节字符字符串的特性。
  • 字符串类是基本字符串类模板(basic_string class template)的一个实例化,它使用char(即字节)作为其字符类型,并使用其默认的char_traits和分配器类型。typedef basic_string string (如果大家对基本的string模板感兴趣,可以通过网站查看)


我自己对String的看法是:把c的char数组封装起来的感觉,然后提供很多方法,char形的顺序表哈哈(以空字符 \0 结尾的字符数组)。 当然这个看法肯定是浅显的。


2.string 的属性

string的属性,在库中可能是比较复杂的,它的字符存储位置不相同,比如小于某个数字的字节,就储存在buffer这个字符数组,大于有存在其他地方,这个比较杂。我这里就简单的把它理解为三个属性(这只是我的简单理解,并非库中真实)。

//基本属性:
char* _str;             //存储字符串的动态数组吧
size_t _size;     //记录字符长度(不包括\0)
size_t _capacity;   //记录数组的容量(最大空间)


3.成员常量

npos这个的值代表无符号整形的最大值:4294967295 。一般在成员函数之间使用,直接使用即可。


4.string 的默认成员函数

构造函数:

代码示例:

方式1:default

1 | //default 默认无参构造:
2 | string s1;  
3 | //错误: string s1(); 
4 | //原因:编译器无法识别这是在声明函数还是对象实例化,会报错!


方式2:copy

1 | //拷贝构造:把s1拷贝给s2(深拷贝)
2 | string s2(s1);


方式3:substring

1 | //拷贝s1,从下标为0的字符开始,拷贝10个长度的字符串
2 | string s3(s1, 0, 10);


方式4:c-string

1 | //拷贝str1字符数组,直到遇到\0:
2 | char str[10] = { 'h','e','l','l','o' };
3 | string s4(str1);


方式5:sequence

1 | //拷贝str1字符数组,前5个字符
2 | string s5(str1, 5);


方式6:fill

1 | //拷贝5个单字符'*':
2 | string s6(5, '*');


方式7:range

1 | //迭代器拷贝 其实相当于指针拷贝 //begin()指向字符数组的第一个元素,end()指向字符数组最后一个元素的下一个位置\0
2 | string s7(s6.begin(), s6.end());


方式8:pointer range

1 | //字符数组 地址拷贝 str1 = "Hello World";
2 | string s8(str1, str1 + sizeof(str1));


析构函数

析构函数释放char* _str;指针申请的堆上空间。


赋值运算符重载

代码示例:


方式1:string

//把s1赋值给s2
string s1;
string s2;           
s2 = s1;                   // 赋值 string类的权限缩小到const string&


方式2:c-string

//赋值一个字符串“hello”
string s1;
s1 = "hello"        // 赋值 char* 权限缩小到 const char*


方式3:character

//赋值一个字符*
string s1;
s1 = '*';            // 赋值 char 权限缩小到 const char

构造赋值小疑问?

提问:string s = "zhang san" ; 这个是什么?

答案:

构造函数+拷贝构造: 这里"zhang san" 是个c-string字符数组,要把它转换到string。就是"zhang san"会调用string的构造sting("zhang san"),然后会再拷贝构造。为什么不是赋值?因为这里是在构造一个对象并初始化,不是赋值。


string s = "zhang san" ; 我自己感觉这样写或者string s("zhang san");这两种写法对我来说最舒服。

5.容量操作

size、length


这两个成员函数是冗余的,两个的效果是一样的,我猜测设计size是为了让string更好的融入STL标准库。

代码示例:

函数: size()、length();
作用:返回string对象的成员数量(_size大小);
返回值:size_t

string s("zhang san");
cout << s.size() << endl;     //9
cout << s.length() << endl;   //9


max_size

代码示例:

函数: max_size();
作用:string对象在当前编译器,操作系统系能开辟的最大空间;
返回值:size_t

string s;
cout << s.max_size() << endl;     //2147483647


不同编译器,操作系统结果可能不同,所以这个函数作用不大。

capacity

代码示例:

函数: capacity();
作用:当前对象的容量,返回_capacity的大小;
返回值:size_t
string s("zhang san");
cout << s << endl;        //zhang san
cout << s.size() << endl;   //9
cout << s.capacity() << endl; //31


resize


在VS,Windows系统下, 开辟空间大于n的空间,具体要看平台,把size扩到或缩到n;

若n大于当前capacity,扩容,追加n-size个指定的字符(没指定也会把size扩到n,用\0沾满)

若n小于capacity,改变size到n,并size位置赋值一个\0;缩小过程,第二个参数没用。

代码示例:

string s("zhang san");
//扩大
s.resize(20,'*');
cout << s << endl;        //zhang san***********
cout << s.size() << endl;   //20
cout << s.capacity() << endl; //31
//缩小
s.resize(5);
cout << s << endl;        //zhang
cout << s.size() << endl;   //5
cout << s.capacity() << endl; //31


reserve


扩容: 传递一个无符号整形n ,改变对象的capacity到n不等(平台不同,扩容不同),开辟一个大小为n的空间,实际开辟n+1不等个。若当前容量大于n,可能会缩小,看平台。

总结一下 : 若你的容量小于n,它会扩容到至少n+1;若n小于当前容量,可能处理,可能不处理。


代码示例:

string s("zhang san");
//缩小
s.reserve(5);
cout << s << endl;        //zhang san
cout << s.size() << endl;   //9
cout << s.capacity() << endl; //15
//扩大
s.reserve(100);
cout << s << endl;        //zhang san
cout << s.size() << endl;   //9
cout << s.capacity() << endl; //111

clear

清除对象中的数据

代码示例:

string s("zhang san");
s.clear();
cout << s << endl;     //无数据


empty

代码示例:

函数: empty();
作用:判断当前对象是否为空,为空返回True,反之;
返回值:bool

string s("zhang san");
cout << s.empty() << endl;        //0


shrink_to_fit (C++11)

代码示例:

函数: shink_to_fit();
作用:把容量缩小到和size一样大小;
返回值:void

string s("zhang san");
s.reserve(1000);
cout << s.size() << endl;   //9
cout << s.capacity() << endl; //1007
s.shrink_to_fit();
cout << s.size() << endl;   //9
cout << s.capacity() << endl; //15


5.元素访问

orerator[]

真爽好吧!下标访问好。[]运算符重载,两个构成函数重载。


代码示例:

函数: orerator[](); 
作用:给它一个初始下标位置,它会返回对应值的引用;
返回值:char&
string s("zhang san");
cout << s[3] << endl ; //n

at

代码示例:

函数: at(); 
作用:给它一个初始下标位置,它会返回对应值的引用;
返回值:char&

string s("zhang san");
cout << s.at(3) << endl ; //n


back (C++11)

代码示例:

函数: back(); 
作用:返回最后一个元素的引用,注意确保对象不为空(会报错);
返回值:char&

string s("zhang san");
cout << s.back() << endl ; //n

front (C++11)

代码示例:

函数: front(); 
作用:返回第一个元素的引用,注意确保对象不为空(会报错);
返回值:char&

string s("zhang san");
cout << s.front() << endl ; //z


7.修改

append

追加操作,在对象最后追加内容

代码示例:

string s1("zhang san");
string s2("li si");
const char* str = "wang wu" ;


方式1:string

//在s1后插入s2
s1.append(s2);    //zhang sanli si


方式2:substring

//在s1后插入s2,从s2下标为2的位置开始的3个字符
s1.append(s2,2,3);    //zhang san si


方式3:c-string

//在s1后插入"li si"
s1.append("li si");    //zhang sanli si


方式4:buffer

//在s1后插入str数组中前三个字符
s1.append(str,3);    //zhang sanwan


方式5:fill

//在s1后插入5个#
s1.append(5,'#');    //zhang san#####


方式6:range

//在s1后插入s2,从开始到结尾的字符.[)
s1.append(s2.begin(),s2.end());    //zhang sanli si


方式7:pointer range

//在s1后插入数组范围开始到第三个位置.[)
s1.append(str,str+3);    //zhang sanwan


push_back

代码示例:

函数: push_back(); 
作用:在最后一个位置后插入一个字符;
返回值:void 

string s("zhang san");
s.push_back('c')
cout << s << endl ; //zhang sanc


operator +=

前面说了那么多,最爽的登场!

代码示例:

string s1("zhang san");
string s2("li si");
s1 += s2;          //zhang sanli si
s1 += "wang wu";         //zhang sanwang wu
s1 += '#';           //zhang san#


assign

覆盖,assign不是追加,而是覆盖。这里我们已经熟悉了这些参数套路,我就不再次一一举例了。

代码示例:

string s1("zhang san");
string s2("li si");
s1.assign(s2);   //li si

insert

具体就是在给定pos位置前,插入一个string、substring、c-string、buffer、fill、character、range

代码示例:

string s("zhang san");
//在下标为3的元素,前面插入"#####"(c-string类型)
s.insert(3,"#####");      //zha#####ng san


erase

代码示例:

方法1:sequence

//删除s下标为6开始的2个的字符 这里给了缺省值哈
string s("zhang san");
s.erase(6,2)    //zhang n


方法2:character

//删除第一个元素。删除iterator处的元素,可以用指针.
string s("zhang san");
s.erase(s.begin())    //hang san


方法3:range

//删除所有元素。范围
string s("zhang san");
s.erase(s.begin(),s.end())    //


replace

swap


交换两个对象,注意是地址也是要交换的,不是只交换值

代码示例:

//交换s1和s2
string s1("zhang san");
string s2("li si");
s1.swap(s2);
cout<<s1<<endl;   //li si
cout<<s2<<endl;     //zhang san

pop_back (C++11)

删除最后一个位置的元素

代码示例:

//尾删
string s1("zhang san");
s1.pop_back();       //zhang sa


8.字符串操作

c_str

代码示例:

//c_str 返回字符串首地址
string s1("zhang san");
cout<<s1.c_str<<endl;     //zhang san
cout<<*s1.c_str()<<endl;      //z


data

这个也是多余的,和c_str一样的作用。

get_allocator

copy


str.copy(char* a, int len , int begin_pos ) 拷贝str对象中从begin_pos开始,长度为len的字符串,到a数组,copy不会自动改\0,所以需要自己修改。返回值为len。若a数组中原本有数据,会从头开始覆盖。

代码示例:

//拷贝s1中从0位置开始的5个字符到a数组,并返回拷贝长度
string s1("zhang san");
char a[100]={'n','h'};
int len = s1.copy(a,5,0);
a[len]='\0';
cout<<a<<endl;

find

在对象中查找是否有与它(str指针内容)相同的,第一个参数可以string,c-string,buffer,character.默认从首位置开始.找到返回找到字符串的的第一个字符的位置,没找到就返回npos。


代码示例:

//s1从首位置开始,找"zhang"字符串
string s1("zhang san");
s1.find("zhang",0);


rfind

与find相反,反着找,默认位置是最后,返回值相同.

find_first_of


给定第一个参数string,c-string,buffer,character,相当于把前面的字符串全部拆分成字符,str如果出现了第一个参数的任意字符,则返回首次出现的位置。默认从第一个数据开始找。往后找。

//s1从下标为0上位置开始,正向找's','q','w'三个字符,返回最后一次位置的下标
string s1("zhang san");
s1.find_first_of("sqw",0);    //6


find_last_of

注意:寻找方向是从pos位置,向前找

代码示例:

//s1从下标为0上位置开始,反向找's','q','w'三个字符,返回最后一次位置的下标
string s1("zhang san");
s1.find_last_of("sqw",0);    //npos;
s1.find_last_of("sqw",6);    //6

find_first_not_of

从第一个位置开始,向后查找,出现不存在在string中组成的字符时,就返回元素位置;若不存在则npos

代码示例:

//在s1中查找不是‘z’,'h','n','g','s'的元素
string s1("zhang san");
s1.find_first_not_of("zhngs",0);    //2  


rfind_last_not_of

从第一个位置开始,向后查找,出现不存在在string中组成的字符时,就返回元素位置;若不存在则npos


代码示例:

//在s1中查找不是‘z’,'h','n','g','s'的元素
string s1("zhang san");
s1.find_last_not_of("zhngs");    //7

substr

获得子字符串,string substr(begin_pos=0,len=npos) 获取从pos位置长度为len的字符串,并返回。

代码示例:

//从s1的首位置后的5个字符作为字符串返回
string s1("zhang san");
string s2 = s1.substr(0,5);  //zhang

compare

9. Iterator

关于迭代器,是需要单独一篇博客来稍微进行分析的,这里只能暂且介绍相关函数。在string类中,我们把typedef char* iterator;,所以这里我们简单的把iterator理解为char*指针。

begin

begin()返回字符串的首字符的地址。


end

end()返回字符串的最后一个元素的下一个位置的地址。也就是\0那个位置。

rbegin

rbegin指向的位置和end()是对称的,也就是最后一个元素的下一个地址。\0的位置。但我们实践发现,解引用后打印的是最后一个元素。那是因为解引用操作的运算符重载,访问的是前一个元素。这是反向迭代器的一个小重点。

代码示例:

string s1("zhang san");
cout << *s1.rbegin();


rend

rend()它指向的位置也和begin对称,指向字符串首地址。

crbegin (C++11)


crend (C++11)

10.string的遍历方法

代码示例:


方法1:常规遍历

for (int i = 0; i < s1.size(); i++)
{
  cout << s1[i] << ' ';
}
cout << endl;


方法2:迭代器遍历

string::iterator it = s1.begin();
  while(it!=s1.end())
  {
    cout << *it << ' ';
    it++;
  }
  cout << endl;

方法3:语法糖(迭代器遍历)

for (auto e : s1)
  {
    cout << e << ' ';
  }
  cout << endl;


语法糖底层也是迭代器遍历,迭代器它是容器之间通用的,也就是说在库里面,迭代器用法都是一样的,但是其底层实现还是存在很多差异的。


总结

这篇博客介绍了string类的所有成员函数,希望对大家有所帮助。博文中部分为进行介绍的函数,后续会补齐。

相关文章
|
1天前
|
C++
C++ string中的函数和常用用法
C++ 中string中的函数和常用用法
10 4
|
9天前
|
设计模式 算法 Java
【c++】STL之stack和queue详解
【c++】STL之stack和queue详解
10 1
|
17天前
|
C语言 C++
【C++】string模拟实现(下)
本文档介绍了自定义`string`类的一些关键功能实现,包括`reserve()`用于内存管理,`push_back()`和`append()`添加字符或字符串,运算符`+=`的重载,以及`insert()`, `erase()`进行插入和删除操作。此外,还涵盖了`find()`查找函数,字符串的比较运算符重载,`substr()`获取子串,`clear()`清除内容,以及流插入和提取操作。常量`npos`用于表示未找到的标记。文档以代码示例和运行结果展示各功能的使用。
|
17天前
|
编译器 程序员 C语言
【C++】string模拟实现
这篇博客探讨了自定义实现C++ `string` 类的关键功能,包括构造、拷贝构造、赋值运算符重载及析构函数。作者强调了理解并实现这些功能对于面试的重要性。博客介绍了`string` 类的头文件`string.h`,其中定义了迭代器、基本成员函数如`swap()`、`size()`、`c_str()`等,并提到了深拷贝概念。此外,还展示了构造函数、析构函数和赋值运算符的实现,以及迭代器的定义与使用。博客还包括对C语言字符串函数的引用,以辅助读者理解实现细节。
|
17天前
|
编译器 C++
【C++】string类的使用④(字符串操作String operations )
这篇博客探讨了C++ STL中`std::string`的几个关键操作,如`c_str()`和`data()`,它们分别返回指向字符串的const char*指针,前者保证以&#39;\0&#39;结尾,后者不保证。`get_allocator()`返回内存分配器,通常不直接使用。`copy()`函数用于将字符串部分复制到字符数组,不添加&#39;\0&#39;。`find()`和`rfind()`用于向前和向后搜索子串或字符。`npos`是string类中的一个常量,表示找不到匹配项时的返回值。博客通过实例展示了这些函数的用法。
|
15天前
|
存储 算法 C++
【C++高阶】探索STL的瑰宝 map与set:高效数据结构的奥秘与技巧
【C++高阶】探索STL的瑰宝 map与set:高效数据结构的奥秘与技巧
24 0
|
16天前
|
存储
数据存储之数组的特点,长度固定,适应变化需求,集合类特点是空间可变,ArrayList泛型,ArrayList<String> array = new ArrayList<String>()
数据存储之数组的特点,长度固定,适应变化需求,集合类特点是空间可变,ArrayList泛型,ArrayList<String> array = new ArrayList<String>()
|
17天前
|
C++
【C++】string类的使用④(常量成员Member constants)
C++ `std::string` 的 `find_first_of`, `find_last_of`, `find_first_not_of`, `find_last_not_of` 函数分别用于从不同方向查找目标字符或子串。它们都返回匹配位置,未找到则返回 `npos`。`substr` 用于提取子字符串,`compare` 则提供更灵活的字符串比较。`npos` 是一个表示最大值的常量,用于标记未找到匹配的情况。示例代码展示了这些函数的实际应用,如替换元音、分割路径、查找非字母字符等。
|
27天前
|
Java UED
Java中String强转int:一种常见的错误和解决方法
在Java中将非数字字符串转换为整数会导致`NumberFormatException`。要解决这个问题,可以使用`try-catch`捕获异常,正则表达式验证数字格式,或利用异常信息提供错误提示。例如,`Integer.parseInt()`会因遇到非数字字符如`&quot;123abc&quot;`而抛出异常,但通过异常处理或正则`\\d+`可确保安全转换。记得在编程时避免直接强转,以防止程序异常中断。
|
18天前
|
安全 Java
Java基础之StringBuffer
【7月更文挑战第1天】 Java中的`StringBuffer`是线程安全的字符串操作类,适合多线程环境,而`StringBuilder`非线程安全,速度更快,适用于单线程。两者提供`append()`、`insert()`、`delete()`等方法修改字符串,避免了频繁创建新对象的性能问题。在不需要线程安全时,推荐使用`StringBuilder`以提高效率。
14 1