C++初阶:初识STL、String类接口详细讲解

1.STL介绍

1.1概念(标准模板库

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架。

1.2STL版本问题(主流有4个)

  • 原始版本

       Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些           代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使用。 HP 版本–所有STL实现版本的始祖。

  • P. J. 版本

        由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改

  • RW版本

       由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一般。

  • SGI版本

       由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版 本。被GCC(Linux)采用,可移植性好,可公开、修改甚至贩卖,从命名风格和           编程风格上看,阅读性非常高。后面学习STL要阅读部分源代码,主要参考的就是这个版本

3. STL的六大组件

  1. 容器(Containers):STL提供了多种容器,包括数组(vector)、链表(list)、双端队列(deque)、集合(set)、映射(map)等。这些容器提供了不同的数据结构,以满足各种不同的需求。
  2. 算法(Algorithms):STL包含了大量的常用算法,如排序、查找、遍历等,这些算法可以用于各种容器,使得对数据的处理变得非常方便。
  3. 迭代器(Iterators):迭代器是STL中用于遍历容器中元素的工具,它提供了一种统一的访问容器元素的方式,使得算法能够适用于不同类型的容器。
  4. 仿函数(Functors):仿函数是一种类对象,它重载了函数调用操作符(),使得可以像函数一样调用这个类对象。STL中的很多算法都可以接受仿函数作为参数,以实现更加灵活的功能。
  5. 适配器(Adapters):STL提供了一些适配器,如栈(stack)、队列(queue)、优先队列(priority_queue),它们是基于其他容器实现的高层次数据结构,提供了特定的操作接口。
  6. 分配器(Allocators):分配器用于管理内存分配和释放,STL提供了一些标准的分配器,同时也允许用户定义自己的分配器,以满足特定的内存管理需求。

2.string类的基本介绍

在 C 语言中,字符串是以 null (也就是"/0")结尾的字符数组,需要手动管理内存和处理字符串操作。string.h 头文件提供了一系列库函数,如 strlenstrcpystrcat 等,用于对字符串进行操作。但是这些函数的确与字符串是分离的,需要手动管理内存,容易出现越界访问等问题。

而在 C++ 标准库中,提供了 std::string 类,它封装了字符串的操作,提供了丰富的成员函数和运算符重载,使得字符串的操作更加方便和安全。std::string 类封装了字符串数据和长度,隐藏了内存管理的细节,提供了自动扩容、内存管理、异常安全性等功能,大大简化了字符串的操作

总结:

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。 

实际上,std::stringC++ 标准库中的一部分,而 STL(标准模板库)是 C++ 标准库的子集,但是由于它和其他 STL 容器(如 std::vectorstd::list)有着相似的使用方式,因此可以将其放在一起学习和使用(出现了.length().size()计算长度的原因,一个是自带的,一个是为了与其他容器相配)

3.string在底层实际是:basic_string模板类的别名 typedef basic_string<char, char_traits, allocator> string,是 basic_string 类模板使用字符类型 char 实例化得到的一个类

4. 不能操作多字节或者变长字符的序列。

5.在使用string类时,必须包含#include头文件以及using namespace std;

  • 我们使用string进行实例化时不用显示实例化,因为本身就是basic_string<char>

3. string类对象的构造(构造函数)

#include<iostream>
using namespace std;
int main()
{
	string s1;
	string s2("abcd");
	string s3(5, 'c');
	string s4(s2);
   
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	return 0;
}

4.访问及遍历操作

4.1operator[ ] ([ ]加下标

重载了下标操作符 [],使得可以像访问数组一样使用下标来访问字符串中的单个字符,同时我们要注意返回值是char&,我们也可以进行更改等其他操作

#include <climits>
#include <iostream>
#include <iterator>
using namespace std;

int main()
{
	string s1("abcdef");
	for (size_t i = 0; i < s1.size(); i++)//遍历
	{
		cout << s1[i] << " " ;
	}
	cout << endl;

	s1[0] = '1';//修改

	cout << s1 << endl;

	return 0;
}

4.2基于范围for

int main()
{
	string s1("abcdef");
	//遍历
	for (auto a: s1)//a代表的是命名,可以随便取
	{
		a = '1';
		cout << a << " ";
	}
	//这里面只是拷贝,a的改变不会影响s1
	cout << endl;
	cout << s1 << endl;

	//但是加上引用之后,a的改变会影响s1
	for (auto& a : s1)
	{
		a = '2';
		cout << a << " ";
	}
	cout << endl;
	cout << s1 << endl;

	return 0;
}

4.3使用迭代器(最推荐使用)

在 C++ 标准库中,std::string 类提供了迭代器,用于遍历字符串中的字符。std::string 类的迭代器类型为 std::string::iterator,它是随机访问迭代器,支持随机访问操作(还没有正式讲到它,大家现在就把他当指针)

int main()
{
	string s1("abcdef");
	//迭代器
	string::iterator a1 = s1.begin();//这相当于下标为0的地址

	while (a1 != s1.end())//s1.end()相当于数组最后一位的下一位
	{
		cout << (*a1);
		++a1;
	}

	return 0;
}

我们还是更倾向于使用迭代器:

  1. 大多数的数据结构都有,但是[]不是都有的
    • 而且使用[]要求物理底层是连续的
  1. 迭代器能跟算法一起用,下面给大家看看结合了reverse()(逆置)的使用
#include <climits>
#include <iostream>
#include <iterator>
#include<list>
using namespace std;

int main()
{
	string s1("abcdef");
	//迭代器
	string::iterator a1 = s1.begin();//这相当于下标为0的地址

	while (a1 != s1.end())//s1.end()相当于字符串最后一位的下一位
	{
		cout << (*a1);
		++a1;
	}
	cout << endl;

	reverse(s1.begin(), s1.end());//反转字符串

	auto a2 = s1.begin();
	while (a2 != s1.end())
	{
		cout << (*a2);
		++a2;
	}

	cout << endl;

	//迭代器还可以在链表中使用
	list<double> l1;//定义一个链表来说明,迭代器两个特点
	//尾插
	l1.push_back(1.1);
	l1.push_back(2.2);
	l1.push_back(3.3);

	list<double>::iterator lit1 = l1.begin();

	while (lit1 != l1.end())
	{
		cout << *lit1 << " ";
		lit1++;
	}
	cout << endl;

	reverse(l1.begin(), l1.end());

	auto lit2 = l1.begin();
	while (lit2 != l1.end())
	{
		cout << *lit2 << " ";
		lit2++;
	}
	cout << endl;


	return 0;
}

5.string的迭代器(Iterator)

5.1介绍

迭代器是一种数据类型。在C++中,迭代器实际上是一种对象,它被设计用于在容器中进行元素的遍历和访问。迭代器为程序员提供了一种抽象的方式来访问容器中的元素,而不用关心容器的底层实现细节

std::string 类提供了多种类型的迭代器,包括正向迭代器(iterator)、常量正向迭代器(const_iterator)、反向迭代器(reverse_iterator)和常量反向迭代器(const_reverse_iterator)

  1. 正向迭代器(iterator):std::string::iterator 类型是用于遍历可修改字符串的迭代器,可以通过 begin() end() 方法获取范围
  2. 常量正向迭代器(const_iterator):std::string::const_iterator 类型是用于遍历不可修改字符串的迭代器,可以通过begin() end() 方法获取范围。
  3. 反向迭代器(reverse_iterator):std::string::reverse_iterator 类型是用于以反向顺序遍历可修改字符串的迭代器,可以通过 rbegin() rend() 方法获取范围
  4. 常量反向迭代器(const_reverse_iterator):std::string::const_reverse_iterator 类型是用于以反向顺序遍历不可修改字符串的迭代器,可以通过 crbegin() crend() 方法获取范围

5.2 begin()和end()(正向和常正向

begin()返回指向容器中第一个元素的迭代器

这是一个重载:

  • iterator begin(); 用于非常量对象,它返回一个迭代器,可以用于修改容器中的元素(可读可写)。
  • const_iterator begin() const; 用于常量对象,它返回一个常量迭代器,用于指向容器中的元素,不允许修改容器中的元素(只读)
int main()
{
	string s1("abcdef");
	const string s2("ertyuio");//常量,不能修改
	
	string::iterator a1 = s1.begin();
	string::const_iterator a2 = s1.end();//权限缩小

	string::const_iterator b1 = s2.begin();
	//string::iterator b2 = s2.end();这种会报错,属于权限扩大

	return 0;
}

end()用于返回指向容器中最后一个元素之后位置的迭代器

也是两个重载,与begin()一样

用法参考上面begin()

5.3rbegin()和rend()(反向和常反向)

rbegin 函数返回一个反向迭代器,指向容器中最后一个元素。反向迭代器允许从容器的末尾向前遍历容器中的元素。

rend 函数返回一个反向迭代器,指向容器中第一个元素之前的位置。通常用于标记反向遍历的结束位置。

这两个也都有重载两个:反向和常量反向

6.string类对象的容量操作

6.1size和length

二者作用其实完全相同: 返回字符串有效字符长度

为什么有两个也是历史原因了.这里推荐大家经常用size(),好与后面联系起来

int main()
{
	string s1("abcde");
	cout << s1.size() << endl;
	cout << s1.length() << endl;
	return 0;
}

6.2capacity

capacity() 是 C++ 中 std::string 类的一个成员函数,用于返回当前字符串对象分配的存储空间大小(即容量)。字符串对象的容量指的是在不重新分配内存的情况下,字符串可以存储的最大字符数

  • 函数: size_t capacity() const noexcept;
  • 返回值: 一个无符号整数,表示当前字符串对象分配的存储空间大小

注意事项: 

  • capacity() 返回的是字符串对象分配的总空间,而不是当前字符串的实际长度。
  • 在执行字符串操作后,capacity() 返回的值可能会大于 size() 返回的值,因为 size() 表示实际存储的字符数,而 capacity() 表示分配的总空间
int main()
{
	string s1("abcde");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	return 0;
}

6.3 reserve和rsize

1.reserve()

  •         函数: void reserve(size_t n) 
  •        功能说明: 用于为字符串预留至少 n 个字符的存储空间,即提前分配足够的空间,但并不改变字符串的实际大小。如果 n 大于当前容量,reserve 可能导致内存重新分配,否则,它只是更新容量而无需重新分配内存

注意事项:

  • reserve 不影响字符串的实际大小,即 size() 的值不会改变。
  • 如果 n 大于当前容量,reserve 可能会导致重新分配内存,但并不会初始化新分配的部分
  • n 大于原字符串的 capacity,此时 reserve 函数会将 capacity 扩容到 n;
  • n 小于等于原字符串的 capacity(会不会缩容,看不同编译器的具体实现)

2.resize()

  • 函数签名: void resize(size_t n, char c = char());或者void resize (size_t n);
  • 功能说明: 将字符串的大小调整为 n,并根据需要插入或删除字符,使得字符串的实际大小等于 n。如果 n 小于当前大小,多余的字符将被删除;如果 n 大于当前大小,字符串将被扩展,并使用字符 c 填充新增的部分; 要是不写就是null characters(\0)

注意事项

  • resize 会修改字符串的实际大小,即 size() 的值会变为 n。(可以缩小,同时也删除了)
  • 如果 n 大于当前大小,新增的部分将用字符 c 填充;要是不写就是null characters(\0)
  • n 小于原字符串的 size,此时 resize 函数会将原字符串的 size 改为 n,也会改变字符串(删除),但不会改变 capacity
  • n 大于原字符串的 size,但小于其 capacity,此时 resize 函数会将 size 后面的空间全部设置为字符 c
  • n 大于原字符串的 capacity,此时 resize 函数会将原字符串扩容,然后将size 后面的空间全部设置为字符 c

7.string类对象的修改操作(+=,insert,erase)

7.1重载的+=(最常用的尾插)

  • 函数: string& operator+=(const string& str);
  • 功能说明: 用于将当前字符串与另一字符串 str 进行连接,即将 str 的内容附加到当前字符串的末尾
int main()
{
	string s1("abcde");
	s1 += "111";
	cout << s1 << endl;
	return 0;
}

7.2insert(效率不是很好)

前者是,在pos这个下标前插入str字符串

后者是,在pos下标前插入nc

int main()
{
	string s1 = "abc";
	cout << s1 << endl;

	s1.insert(0, "111");//0前面不就相当于头插嘛
	cout << s1 << endl;

	s1.insert(0, 2, 'x');
	cout << s1 << endl;
	return 0;
}

各种详细的用法:

int main()
{
	std::string str = "to be question";
	std::string str2 = "the ";
	std::string str3 = "or not to be";
	std::string::iterator it;

	//在pos数字(下标)前插入str2
	cout << str.insert(6, str2) << endl;         // to be (the )question
	//在pos数字(下标)前插入str3字符串中下标为3的4个字符
	cout << str.insert(6, str3, 3, 4) << endl;;  // to be (not )the question
	str.insert(10, "that is cool", 8);           // to be not (that is )the question
	str.insert(10, "to be ");                    // to be not (to be )that is the question
	str.insert(15, 1, ':');                      // to be not to be(:) that is the question
	it = str.insert(str.begin() + 5, ',');       // to be(,) not to be: that is the question
	str.insert(str.end(), 3, '.');               // to be, not to be: that is the question(...)
	str.insert(it + 2, str3.begin(), str3.begin() + 3); // (or )

	std::cout << str << '\n';
	return 0;

7.3 erase(任意位置删除)

删除从下标pos处开始算起的len个字符

int main()
{
	string s1 = "abc";
	cout << s1 << endl;

	s1.insert(0, "111");//0前面不就相当于头插嘛
	cout << s1 << endl;

	s1.insert(0, 2, 'x');
	cout << s1 << endl;

	s1.erase(0, 5);//从0处,删除5个
	cout << s1 << endl;

	return 0;
}

补充:npos

npos 是 C++ 中 std::string 类的一个静态成员变量,表示无效或不存在的位置。通常用于标识字符串查找等操作未找到匹配项的情况。npos 的类型是 size_t,它是一个无符号整数类型

                                                                                  npos一般是指一个非常大的正数

8.String operations函数(find,rfind,substr)

8.1find

find 用于返回 一个字符或一个字符数组或一个string对象 在 string 中首次出现的位置(返回下标),如果找不到就返回 npos

8.2rfind

整体跟find类似。从后往前找,找到一个字符或一个字符数组或一个string对象最后一次出现的位置,如果找不到就返回 npos

8.3substr(截取字符串)

pos处开始截取len长度(默认的话,截取到最后)

int main()
{
    string str = "We think in generalities, but we live in details.";
    

    string str2 = str.substr(3, 5);     // "think"
    cout << str2 << endl;

    size_t pos = str.find("live");      // pos='l'的下标
    string str3 = str.substr(pos);     // ”live in details.“ -----后面什么也不给,使用缺省值
    cout << str3 << endl;
    

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值