【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类的所有成员函数,希望对大家有所帮助。博文中部分为进行介绍的函数,后续会补齐。

相关文章
|
24天前
|
C语言 C++ 容器
【c++丨STL】string模拟实现(附源码)
本文详细介绍了如何模拟实现C++ STL中的`string`类,包括其构造函数、拷贝构造、赋值重载、析构函数等基本功能,以及字符串的插入、删除、查找、比较等操作。文章还展示了如何实现输入输出流操作符,使自定义的`string`类能够方便地与`cin`和`cout`配合使用。通过这些实现,读者不仅能加深对`string`类的理解,还能提升对C++编程技巧的掌握。
49 5
|
24天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
38 2
|
1月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
83 5
|
1月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
80 4
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
44 0
java基础(13)String类
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
67 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
50 2
|
3月前
|
安全 Java
String类-知识回顾①
这篇文章回顾了Java中String类的相关知识点,包括`==`操作符和`equals()`方法的区别、String类对象的不可变性及其好处、String常量池的概念,以及String对象的加法操作。文章通过代码示例详细解释了这些概念,并探讨了使用String常量池时的一些行为。
String类-知识回顾①
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
26 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
64 4