【C++详解】——初识STL(string类的使用)

news2024/9/20 22:23:20

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

在这里插入图片描述


目录

  • 🕒 1. string 概述
  • 🕒 2. 标准库中的string类
  • 🕒 3. string的常用接口
    • 🕘 3.1 初始化
    • 🕘 3.2 Iterators
      • 🕤 3.2.1 正向迭代器
      • 🕤 3.2.2 反向迭代器
      • 🕤 3.2.3 const 迭代器
    • 🕘 3.3 容量
      • 🕤 3.3.1 扩容
    • 🕘 3.4 元素访问
    • 🕘 3.5 修改
    • 🕘 3.6 操作
  • 🕒 4. string类的应用
    • 🕘 4.1 三种遍历方式
    • 🕘 4.2 替换空格
    • 🕘 4.3 取文件名后缀
    • 🕘 4.4 getline的应用
  • 🕒 5. string类的模拟实现

🕒 1. string 概述

string,就是比C语言好用的字符串,在C++中作为替代char*的存在,和前者比起来,不必担心内存是否足够、字符串长度等等,而且作为一个泛型类出现,他集成的操作函数足以完成我们大多数情况下的需要。

string类本身就是一个模板,是因为字符串的数组涉及编码问题,字符数组编码不同。所以需要模板。

u16string:表示两个字节
u32string:表示四个字节

这里简单了解一下编码
ASCII码 美国信息交换标准码。 ASCII码表是计算机存值和文字符号的对应关系 只有256个字符
在这里插入图片描述
Unicode 万国码:Unicode是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码
包括了utf-8,utf-16,utf-32 utf-8兼容了ASCII,utf-8使用比较普遍,也比较节省空间

gbk 国标:针对中文而设计的编码。采用双字节编码。

🕒 2. 标准库中的string类

string是个类,是模板的实例化

C++中对于string的定义为:typedef basic_string<char> string; 也就是说C++中的string类是一个泛型类,由模板而实例化的一个标准类,本质上不是一个标准数据类型。

在使用 string 类时,必须包含 #include<string> 以及 using namespace std;

🔎 C++在线文档关于string类的介绍

🕒 3. string的常用接口

🕘 3.1 初始化

 函数名称   功能说明   string() (重点)   构造空的string类对象,即空字符串   string(const char* s) (重点)   用C-string来构造string类对象   string(const char* s, size_t n)   从s中选择前n个字符初始化   string(size_t n, char c)   string类对象中包含n个字符c   string(const string& str) (重点)  拷贝构造函数   string(const string& str, size_t pos, size_t len = npos)   在str中从pos开始拷贝len个字符,如果len>str长度,就取完,npos是缺省值(-1),即取完  \begin{array}{|c|c|} \hline \text { 函数名称 } & \text { 功能说明 } \\ \hline \text { string() (重点) } & \text { 构造空的string类对象,即空字符串 } \\ \hline \text { string(const char* s) (重点) } & \text { 用C-string来构造string类对象 } \\ \hline \text { string(const char* s, size\_t n) } & \text { 从s中选择前n个字符初始化 } \\ \hline \text { string(size\_t n, char c) } & \text { string类对象中包含n个字符c } \\ \hline \text { string(const string\& str) (重点)} & \text { 拷贝构造函数 } \\ \hline \text { string(const string\& str, size\_t pos, size\_t len = npos) } & \text { 在str中从pos开始拷贝len个字符,如果len>str长度,就取完,npos是缺省值(-1),即取完 } \\ \hline \end{array}  函数名称  string() (重点)  string(const char* s) (重点)  string(const char* s, size_t n)  string(size_t n, char c)  string(const string& str) (重点) string(const string& str, size_t pos, size_t len = npos)  功能说明  构造空的string类对象,即空字符串  C-string来构造string类对象  s中选择前n个字符初始化  string类对象中包含n个字符 拷贝构造函数  str中从pos开始拷贝len个字符,如果len>str长度,就取完,npos是缺省值(-1),即取完 

void test1()
{
	string s1;				 // 构造空字符串
	string s2("广州市海珠区"); // 用字符数组来构造string类对象
	string s3 = "广州市海珠区";// 同上
	string s4(10, '*');		 // 用10个*初始化
	string s5(s2);			 // 拷贝构造
	string s6 = s2;			 // 拷贝构造
	//cout << s1 << endl;
	cout << s2 << endl;		 // 流提取
	//cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << " " << s6 << endl;
	s2 += "滨江街道";
	cout << s2 << endl;
	string s7("Hello World!", 5);
	cout << s7 << endl;

	string s8(s7, 2, 3);	// 在s7中从第2个位置开始走3个字符
	cout << s8 << endl;

	string s9(s7, 2, 30);	// 在s7中从第2个位置开始走30个字符
	cout << s9 << endl;

	string s10(s7, 2 );	// 在s7中从第2个位置开始走完(缺省值是-1,即可以认为全部走完)
	cout << s10 << endl;
}

在这里插入图片描述

🕘 3.2 Iterators

Iterators 是C++中的迭代器,它可以遍历读写数据,是通用的访问形式(不止string,后面vector、list都还是会用到),大家可以把它当成指针来理解,当然,并不是所有迭代器的底层都是用指针实现的。值得注意的是,这里不能使用char*,因为迭代器的底层不一定是char*的方式实现(虽然VS可以,但是其他平台未必相同)。

 函数名称   功能说明   begin()   返回一个指向字符串中第一个字符的迭代器   end()   返回一个指向字符串最后一个字符下一个位置(’  \ 0 ’)的迭代器   rbegin()   反向迭代器,返回一个指向字符串最后一个字符下一个位置(’  \ 0 ’)的迭代器   rend()   反向迭代器,返回一个指向字符串中第一个字符的迭代器  \begin{array}{|c|c|} \hline \text { 函数名称 } & \text { 功能说明 } \\ \hline \text { begin() } & \text { 返回一个指向字符串中第一个字符的迭代器 } \\ \hline \text { end() } & \text { 返回一个指向字符串最后一个字符下一个位置(’ } \backslash 0 \text{’)的迭代器 } \\ \hline \text { rbegin() } & \text { 反向迭代器,返回一个指向字符串最后一个字符下一个位置(’ } \backslash 0 \text{’)的迭代器 } \\ \hline \text { rend() } & \text { 反向迭代器,返回一个指向字符串中第一个字符的迭代器 } \\ \hline \end{array}  函数名称  begin()  end()  rbegin()  rend()  功能说明  返回一个指向字符串中第一个字符的迭代器  返回一个指向字符串最后一个字符下一个位置(’ \0’)的迭代器  反向迭代器,返回一个指向字符串最后一个字符下一个位置(’ \0’)的迭代器  反向迭代器,返回一个指向字符串中第一个字符的迭代器 

注意:为了使 const 对象也能调用,每个迭代器函数都设计了 const 版本

这里我们可以小结一下功能函数需要的版本:

  1. 只读功能函数 const版本(比如:size
  2. 只写功能函数 非const版本(比如:push_back
  3. 读写功能函数 const+非const版本(比如:operator[]
> std::string::begin
      iterator begin();	        //用法1
const_iterator begin(); 		//用法2

> std::string::end
      iterator end();		    //用法1
const_iterator end();			//用法2

> std::string::rbegin
      reverse_iterator rbegin();		//用法1
const_reverse_iterator rbegin();		//用法2

> std::string::rend
      reverse_iterator rend();		    //用法1
const_reverse_iterator rend();			//用法2

🕤 3.2.1 正向迭代器

void testForward()
{
	string s1("1234");
	string::iterator it1 = s1.begin();//s.begin() 返回字符串s第一个字符的位置
	while (it1 != s1.end())//s.end()  返回字符串s最后一个字符串的后一个位置
	{
	    *it1 += 1;
	    ++it1;
	}
	it1 = s1.begin();
	while (it1 != s1.end())
	{
	    cout << *it1 << " ";
	    ++it1;
	}           // 输出:2 3 4 5 
}

🕤 3.2.2 反向迭代器

反向迭代器访问顺序与默认的迭代器相反。

void testBack()
{
	string s1("1234");
	string::reverse_iterator rit = s1.rbegin();
	// auto rit = s1.rbegin();  // 简化写法
	while (rit != s1.rend())
	{
	    cout << *rit << " ";
	    ++rit;
	} // 输出: 4 3 2 1
}

对于定义名过长的问题,我们也可以用auto去定义变量rit接收rbegin()。不过嘛,这样可读性就会变差(学了Python就懂了[doge])。

🕤 3.2.3 const 迭代器

顾名思义,只能遍历,不能修改容器数据,包括正向和反向

void Print(const string& s)
{
	// 遍历读,不支持写
	string::const_iterator it = s.begin();	// 正向
	//const string::iterator it = s.begin();	// 错误写法
	while (it != s.end())
	{
		// *it += 1;			// 编译不通过

		cout << *it << " ";		// 输出:1 2 3 4
		++it;
	}
	cout << endl;

	//string::const_reverse_iterator rit = s.rbegin();
	auto rit = s.rbegin();	// 反向
	while (rit != s.rend())
	{
		cout << *rit << " ";	// 输出:4 3 2 1
		++rit;
	}
	cout << endl;
}

int main()
{
	string s1("1234");
	Print(s1);	// 传引用
	return 0;
}

这里看过C++文档网站的可能要问了,还有个带c的版本没讲嘞。
其实就是名称区分一下,让你一眼就看出是const版本的迭代器。

🕘 3.3 容量

 函数名称   功能说明   size(重点)   返回字符串有效字符长度   length   返回字符串有效字符长度  capacity   返回空间总大小   empty (重点)  检测字符串是否为空串,是返回true,否则返回false   clear (重点)   清空有效字符   reserve (重点)   为字符串预留空间   resize (重点)   将有效字符的个数改成n个,多出的空间用 \ 0  或指定字符填充   shrink_to_fit   改变capacity使其与size保持一致  \begin{array}{|c|c|} \hline \text { 函数名称 } & \text { 功能说明 } \\ \hline \text { size(重点) } & \text { 返回字符串有效字符长度 } \\ \hline \text { length } & \text { 返回字符串有效字符长度} \\ \hline \text { capacity } & \text { 返回空间总大小 } \\ \hline \text { empty (重点)} & \text { 检测字符串是否为空串,是返回true,否则返回false } \\ \hline \text { clear (重点) } & \text { 清空有效字符 } \\ \hline \text { reserve (重点) } & \text { 为字符串预留空间 } \\ \hline \text { resize (重点) } & \text { 将有效字符的个数改成n个,多出的空间用} \backslash 0 \text{ 或指定字符填充 } \\ \hline \text { shrink\_to\_fit } & \text { 改变capacity使其与size保持一致 } \\ \hline \end{array}  函数名称  size(重点)  length  capacity  empty (重点) clear (重点)  reserve (重点)  resize (重点)  shrink_to_fit  功能说明  返回字符串有效字符长度  返回字符串有效字符长度 返回空间总大小  检测字符串是否为空串,是返回true,否则返回false  清空有效字符  为字符串预留空间  将有效字符的个数改成n个,多出的空间用\0 或指定字符填充  改变capacity使其与size保持一致 

size和length
由于历史原因,最开始只有length,但随着STL的扩充,其他如树就不太适合用“长度”来描述,为了普适性,统一用size来实现。自然,其他比如vector这些就没有length了。

clear

  • clear 函数用来清空字符串,即将 size 改为0,至于是否会改变 capacity,标准也未规定,在VS2019是没有改变的

resize
resize 函数用来调整字符串大小,它一共分为三种情况:

  • n < size,删除数据,将原字符串的 size 改为 n,但不会改变 capacity;
  • n > size,但 < capacity,插入数据,将 size 后面的空间全部设置为\0
  • n > capacity,扩容+插入数据,然后将size 后面的空间全部设置为\0

注:s.resize(n,'c'),其中n > size,但 < capacity:表示插入数据,将 size 后面的空间全部设置为字符c

reserve
reserve 用来扩容与预留空间,相当于C语言中的 realloc 函数,它分两种情况:

  • n 大于原字符串的 capacity,此时 reserve 函数会将 capacity 扩容到 n;
  • n 小于等于原字符串的 capacity,标准未规定是否要缩容 (VS下不缩容);

🕤 3.3.1 扩容

我们可以观察一下尾插是怎么扩容的。

void TestPushBack()
{
	string s;
	// s.reserve(100)  // 直接设置指定容量
	size_t sz = s.capacity();
	for (int i = 0; i < 100; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << sz << " ";		// 输出:31 47 70 105
		}
	}
}

可以看到,大概是以1.5倍增长,其中容量没有计入\0,即32-1=31。
不同平台的n倍值不同,比如g++是2倍(计入\0)。

🕘 3.4 元素访问

 函数名称   功能说明   operator[] (重点)   返回pos位置的字符,const string类对象调用,越界就assert报错   at   功能同上,但是越界了会抛异常   begin+ end   begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器  rbegin + rend   rbegin获取一个字符的迭代器 + rend获取最后一个字符下一个位置的迭代器   范围for   C++11支持更简洁的范围for的新遍历方式  \begin{array}{|c|c|} \hline \text { 函数名称 } & \text { 功能说明 } \\ \hline \text { operator[] (重点) } & \text { 返回pos位置的字符,const string类对象调用,越界就assert报错 } \\ \hline \text { at } & \text { 功能同上,但是越界了会抛异常 } \\ \hline \text { begin+ end } & \text { begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器} \\ \hline \text { rbegin + rend } & \text { rbegin获取一个字符的迭代器 + rend获取最后一个字符下一个位置的迭代器 } \\ \hline \text { 范围for } & \text { C++11支持更简洁的范围for的新遍历方式 } \\ \hline \end{array}  函数名称  operator[] (重点)  at  begin+ end  rbegin + rend  范围for  功能说明  返回pos位置的字符,const string类对象调用,越界就assert报错  功能同上,但是越界了会抛异常  begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 rbegin获取一个字符的迭代器 + rend获取最后一个字符下一个位置的迭代器  C++11支持更简洁的范围for的新遍历方式 

🕘 3.5 修改

 函数名称   功能说明   push_back   在字符串后尾插字符c   append   在字符串后追加一个字符串  operator+= (重点)   在字符串后追加字符串str   assign   清除原内容并赋值  replace   选中被赋值区域后替换  insert   向在字符串的 pos 处插入数据   erase   从 pos 位置开始向后删除 len 个字符  \begin{array}{|c|c|} \hline \text { 函数名称 } & \text { 功能说明 } \\ \hline \text { push\_back } & \text { 在字符串后尾插字符c } \\ \hline \text { append } & \text { 在字符串后追加一个字符串} \\ \hline \text { operator+= (重点) } & \text { 在字符串后追加字符串str } \\ \hline \text { assign } & \text { 清除原内容并赋值} \\ \hline \text { replace } & \text { 选中被赋值区域后替换} \\ \hline \text { insert } & \text { 向在字符串的 pos 处插入数据 } \\ \hline \text { erase } & \text { 从 pos 位置开始向后删除 len 个字符 } \\ \hline \end{array}  函数名称  push_back  append  operator+= (重点 assign  replace  insert  erase  功能说明  在字符串后尾插字符 在字符串后追加一个字符串 在字符串后追加字符串str  清除原内容并赋值 选中被赋值区域后替换 向在字符串的 pos 处插入数据   pos 位置开始向后删除 len 个字符 

void test_stringModify()
{
	string s1("hello world");
	s1 += '!';
	s1.push_back(' ');
	s1 += "hello world";	// 推荐这种写法
	cout << s1 << endl;		// 输出:hello world! hello world

	string s2("!!!");
	s1 += s2;
	cout << s1 << endl;		// 输出:hello world! hello world!!!
	
	s1.erase(13, 14);		// (pos, len)
	s1.insert(0, "C++ ");	// (pos, str)
	cout << s1 << endl;		// 输出:C++ hello world!

	s1.replace(0, 3, "Python");	//
	cout << s1 << endl;		// 输出:Python hello world!
	s1.assign("Hello C++", 5);	// 清除原内容并赋值前5个
	cout << s1 << endl;		// 输出:Hello
}

🕘 3.6 操作

 函数名称   功能说明   assign   清除原内容并赋值  replace   选中被赋值区域后替换  insert   向在字符串的 pos 处插入数据   erase   从 pos 位置开始向后删除 len 个字符   c_str(重点)   返回C格式字符串   find + npos(重点)   从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置   rfind   从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置   substr   在str中从pos位置开始,截取n个字符,然后将其返回  \begin{array}{|c|c|} \hline \text { 函数名称 } & \text { 功能说明 } \\ \hline \text { assign } & \text { 清除原内容并赋值} \\ \hline \text { replace } & \text { 选中被赋值区域后替换} \\ \hline \text { insert } & \text { 向在字符串的 pos 处插入数据 } \\ \hline \text { erase } & \text { 从 pos 位置开始向后删除 len 个字符 } \\ \hline \text { c\_str(重点) } & \text { 返回C格式字符串 } \\ \hline \text { find + npos(重点) } & \text { 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置 } \\ \hline \text { rfind } & \text { 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置 } \\ \hline \text { substr } & \text { 在str中从pos位置开始,截取n个字符,然后将其返回 } \\ \hline \end{array}  函数名称  assign  replace  insert  erase  c_str(重点 find + npos(重点 rfind  substr  功能说明  清除原内容并赋值 选中被赋值区域后替换 向在字符串的 pos 处插入数据   pos 位置开始向后删除 len 个字符  返回C格式字符串  从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置  从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置  str中从pos位置开始,截取n个字符,然后将其返回 

void test_stringOperations()
{
	string s1("hello world");
	string s2("hello world! hello world");	
	size_t pos = s2.find(' ');
	while (pos != string::npos)
	{
		s2.replace(pos, 1, "~~~");	// 把空格替换为~~~
		pos = s2.find(' ', pos + 3);
	}
	cout << s2 << endl;		// 输出:hello~~~world!~~~hello~~~world

	string file("test.cpp");
	FILE* fout = fopen(file.c_str(), "r");
	assert(fout);

	char ch = fgetc(fout);
	while (ch != EOF)
	{
		cout << ch;
		ch = fgetc(fout);
	}
	fclose(fout);
}

关于c_str的用途简介
一些线程,网络,Linux内核等都是通过C实现的,因此c_str很好的充当了一个C++中string与C之间的互通,因为我们知道,对于string定义的变量名,不是内部字符串的地址,因此就出现了c_str()返回内容的地址,从而解决这个问题。

🕒 4. string类的应用

🕘 4.1 三种遍历方式

有了迭代器之后,我们就可以使用迭代器来遍历与修改字符串了:

void testgo()
{
	string s1("1234");	
	// 遍历它
	// 1.下标[]
	for (size_t i = 0; i < s1.size(); ++i)
	{
		s1[i]++;	// 每个数字+1,实际上这是operator[]()的运算符重载
	}
	cout << s1 << endl;		// 输出:2345

	// 2.范围for
	for (auto& ch : s1)
	{
		ch--;		// 每个数字-1
	}
	cout << s1 << endl;		// 输出:1234

	// 反转一下
	size_t begin = 0, end = s1.size() - 1;
	while (begin < end)
	{
		swap(s1[begin++], s1[end--]);
	}
	cout << s1 << endl;		// 输出:4321

	reverse(s1.begin(), s1.end());	// 一键反转
	cout << s1 << endl;		// 输出:1234

	// 3、迭代器  -- 通用的访问形式
	string::iterator it1 = s1.begin();
	while (it1 != s1.end())
	{
		*it1 += 1;
		++it1;
	}

	it1 = s1.begin();
	while (it1 != s1.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;

	vector<int> v;
	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << " ";	// 输出:2 3 4 5
		vit++;
	}
	cout << endl;

	list<int> lt;				// 仅演示。还没放数据
	list<int>::iterator ltit = lt.begin();
	while (ltit != lt.end())
	{
		cout << *ltit << " ";
		ltit++;
	}
	cout << endl;
}

🕘 4.2 替换空格

🔎 替换空格

class Solution {
public:
    string replaceSpace(string s) {
        size_t pos = s.find(' ');
        while(pos != string::npos)
        {
            s.replace(pos, 1, "%20");
            pos = s.find(' ',pos+3); // 优化
        }
        return s;
    }
};

🕘 4.3 取文件名后缀

void test_stringSuffix()		// 取文件后缀
{
	// "Test.cpp"		①
	// "Test.cpp.tar"	②
	string file;
	cin >> file;
	//size_t pos = file.find('.');		// 遇到②的情况就不行了
	size_t pos = file.rfind('.');		// 倒着找
	if (pos != string::npos)
	{
		//string suffix = file.substr(pos, file.size() - pos);
		string suffix = file.substr(pos);		// 缺省参数
		cout << suffix << endl;
	}
}

🕘 4.4 getline的应用

🔎 HJ1 字符串最后一个单词的长度
getline可以获取一行字符串,到换行符时才结束输入

#include <iostream>
using namespace std;

int main() {
    string str;
    //cin>>str;
    getline(cin, str);

    size_t pos = str.rfind(' ');
    cout<<str.size()-pos-1<<endl;

    return 0;
}

🕒 5. string类的模拟实现

// string.h
#pragma once
#include<iostream>
#include<string.h>
#include<assert.h>

using namespace std;
namespace mystring
{
	class string
	{
	public:
		typedef char* iterator;

		iterator begin()
		{
			return _str;
		}

		iterator end()
		{
			return _str + _size;
		}

		/*string()
		{
			_str = new char[1];
			_str[0] = '\0';
			_capacity = _size = 0;
		}*/

		// '\0'		char,ascll为0
		// "\0"		相当于两个\0
		// ""		相当于一个\0
		string(const char* str = "")	// 对空对象的缺省参数
		{
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];

			strcpy(_str, str);
		}

		void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		// 拷贝构造的现代写法
		// s2(s1)
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str); // 构造函数
			//this->swap(tmp);
			swap(tmp);
		}	//tmp作为临时变量出了作用域会自动调用析构

		// s2(s1)
		// 拷贝构造的传统写法
		/*string(const string& s)
		{
		_str = new char[s._capacity + 1];
		_capacity = s._capacity;
		_size = s._size;

		strcpy(_str, s._str);
		}*/

		// s1 = s3;
		//string& operator=(const string& s)
		//{
		//	if (this != &s)
		//	{
		//		//string tmp(s._str);
		//		string tmp(s);
		//		swap(tmp);
		//	}

		//	return *this;
		//}

		// s1 = s3;
		string& operator=(string s)
		{
			swap(s);
			return *this;
		}

		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

		const char* c_str() const
		{
			return _str;
		}

		size_t size() const
		{
			return _size;
		}

		size_t capacity() const
		{
			return _capacity;
		}

		// 普通对象:可读可写
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

		// const对象:只读
		const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

		void reserve(size_t n)	// 容量扩展
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;

				_capacity = n;
			}
		}

		void resize(size_t n, char ch = '\0')
		{
			if (n > _size)
			{
				reserve(n);
				for (size_t i = _size; i < n; ++i)
				{
					_str[i] = ch;
				}

				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				_str[n] = '\0';
				_size = n;
			}
		}

		void push_back(char ch)
		{
			if (_size == _capacity)
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}

			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

		void append(const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			strcpy(_str + _size, str);
			_size += len;
		}

		string& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}

		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}

		string& insert(size_t pos, char ch)
		{
			assert(pos <= _size);

			if (_size == _capacity)
			{
				size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
				reserve(newCapacity);
			}

			// 挪动数据
			/*int end = _size;
			while (end >= (int)pos)
			{
			_str[end + 1] = _str[end];
			--end;
			}*/

			size_t end = _size + 1;
			while (end > pos)
			{
				_str[end] = _str[end - 1];
				--end;
			}

			_str[pos] = ch;
			++_size;

			return *this;
		}

		string& insert(size_t pos, const char* str)
		{
			size_t len = strlen(str);
			if (_size + len > _capacity)
			{
				reserve(_size + len);
			}

			/*	int end = _size;
				while (end >= (int)pos)
				{
				_str[end + len] = _str[end];
				--end;
				}*/

			size_t end = _size + len;
			while (end > pos + len - 1)
			{
				_str[end] = _str[end - len];
				--end;
			}

			strncpy(_str + pos, str, len);
			_size += len;

			return *this;
		}

		string& erase(size_t pos, size_t len = npos)
		{
			assert(pos < _size);

			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str + pos, _str + pos + len);
				_size -= len;
			}

			return *this;
		}

		size_t find(char ch, size_t pos = 0) const
		{
			assert(pos < _size);
			while (pos < _size)
			{
				if (_str[pos] == ch)
				{
					return pos;
				}

				++pos;
			}
			return npos;
		}

		size_t find(const char* str, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* ptr = strstr(_str + pos, str);
			if (ptr == nullptr)
			{
				return npos;
			}
			else
			{
				return ptr - _str;
			}
		}

		void clear()
		{
			_size = 0;
			_str[0] = '\0';
		}

	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		const static size_t npos = -1;		// 特例

		/*const static size_t N = 10;
		int a[N];*/
		// const static double x;
	};

	ostream& operator<<(ostream& out, const string& s)
	{
		for (size_t i = 0; i < s.size(); ++i)
		{
			out << s[i];
		}

		return out;
	}

	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		//char ch = in.get();
		//while (ch != ' ' && ch != '\n')
		//{
		//	s += ch;
		//	//in >> ch;
		//	ch = in.get();
		//}

		char buff[128] = { '\0' };	// 防止扩容代价大,通过buff暂时储存
		size_t i = 0;
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			if (i == 127)
			{
				// 满了
				s += buff;
				i = 0;
			}

			buff[i++] = ch;

			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

OK,以上就是本期知识点“初识STL”的知识啦~~ ,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟📌~
💫如果有错误❌,欢迎批评指正呀👀~让我们一起相互进步🚀
🎉如果觉得收获满满,可以点点赞👍支持一下哟~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182652.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【小程序 | 黑马优选】tabBar、首页制作

文章目录tabBar制作首页制作配置网络请求制作轮播图效果渲染轮播图的UI解构配置小程序分包点击轮播图跳转到商品详情页面封装 uni.$showMsg() 方法分类导航区制作楼层区域制作tabBar制作 在 pages 目录中&#xff0c;创建首页(home)、分类(cate)、购物车(cart)、我的(my) 这 4…

windows下zookeeper搭建

程序包下载 官网下载地址 下载解压后如下&#xff01; 注意&#xff0c;zookeeper需要java环境&#xff0c;如果配置了JAVA_HOME那最好&#xff0c;如果没配置就会出现点击bin下的zkServer.cmd后CMD窗口一闪而过 修改配置 如果本地端口没有特别要求可以直接复制conf下的zo…

多臂PEG衍生物8-Arm PEG-SAA,8-Arm PEG-Succinamide Acid,八臂PEG丁二酸酰胺

一&#xff1a;产品描述 1、名称 英文&#xff1a;8-Arm PEG-SAA&#xff0c;8-Arm PEG-Succinamide Acid 中文&#xff1a;八臂-聚乙二醇-丁二酸酰胺 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Carboxylic acid PEG Multi-arm PEGs 4、分子量&#xff1a;可定制…

IDEA新建js项目(hello)和执行js脚本

一)、安装Node.js具体操作参考:https://blog.csdn.net/xijinno1/article/details/128774375二)、IDEA中新建js项目(hello world)1.按照下图&#xff0c;新建js项目2.选中示例代码文件后点击鼠标右键&#xff0c;选中菜单栏中的 运行* 栏目运行代码(第一次运行代码的方式)3.若是…

【版本控制】Git快速上手

Do you know what Git is&#xff1f; 一.引入 (1) 作用 Git 是一个分布式版本控制系统&#xff0c;主要是用于管理开发过程中的源代码文件&#xff08;Java类&#xff0c;xml文件&#xff0c;html页面等&#xff09;。可用于代码回溯&#xff0c;版本切换&#xff0c;多人协作…

AcWing 292. 炮兵阵地(状态压缩DP)

AcWing 292. 炮兵阵地&#xff08;状态压缩DP&#xff09;一、题目二、思路1、分析2、状态表示3、状态转移4、循环设计5、初末状态三、代码一、题目 二、思路 1、分析 这道题的话和我们之前讲解的AcWing 327. 玉米田&#xff08;状态压缩DP&#xff09;和AcWing 1064. 小国王…

Jenkins环境搭建与实战

Jenkins环境搭建与实战1、Jenkins2、GItLab的安装2.1、安装依赖2.1.1、CentOS8安装报错2.1.2、找不到对应包安装报错2.2、配置镜像2.3、安装gitlab3、安装Jenkins4、Maven安装4.1、出现报错 The JAVA_HOME environment variable is not defined correctly的错误5、Jenkins 通过…

SWIFT Framework .NET 2023

SWIFT Framework .NET 2023 Latest 2023 specification messages.Improves parsing..NET Framework 4.8 release.Performance updates.Improves handling of special characters. SWIFT Framework.NET是一个用于在组织信息系统基础架构中捕获、验证和处理SWIFT消息的系统。SWI…

3.5主存储器与CPU的连接

文章目录一、引子二、单块存储芯片与CPU的连接三、多块存储芯片与CPU的连接1.现代计算机2.命名3.增加主存的存储字长--位扩展&#xff08;1&#xff09;单块&#xff08;2&#xff09;多块4.增加主存的存储字数--字扩展&#xff08;1&#xff09;单块&#xff08;2&#xff09;…

19行列式公式和代数余子式

行列式公式 学习了关于行列式的这么多性质&#xff0c;现在我们有能力推导二阶行列式公式了&#xff1a; 观察上面的推导过程&#xff0c;不难发现&#xff0c;行列式的值等于使用性质3.b 分解后所得的那些非零行列式的和&#xff0c;所谓的非零行列式也即该行列式各行各列都…

【算法基础】大整数加减乘除法(高精度)

大整数的思想:用数组存储大整数(超长整数),比如存储1000位的整数只需要开辟一个长度为1000的数组(C++通常使用vector),今天将通过OJ例题来介绍高精度问题。(完全0基础的先建议自主学习一下,本博客默认已了解大致思想) 一、 大整数加法(大整数 + 大整数) (一)Qu…

6 逻辑斯蒂回归

文章目录回归问题和分类问题问题提出逻辑回归二分类问题逻辑函数与线性回归方程的不同模型变化loss函数不同BCEloss函数的介绍课程代码课程来源&#xff1a; 链接课程文本来源借鉴&#xff1a; 链接以及&#xff08;强烈推荐&#xff09; Birandaの回归问题和分类问题 有监督学…

Docker安装Tomcat服务器

Docker安装Tomcat服务器查看tomcat镜像下载 tomcat镜像启动tomcat容器浏览器访问容器中的tomcat1 查看ip2 查看容器是否启动3 进入容器重启容器浏览器访问查看tomcat镜像 docker search tomcat下载 tomcat镜像 咱直接下载最近版本的tomcat镜像 docker pull tomcat查看一下本…

芯片验证系列——激励(stimulus)

对于芯片验证&#xff0c;主要的挑战在于&#xff1a;1.如何打出所有可能的激励灌给DUT&#xff1b;2.如何在各种可能得激励情况下&#xff0c;判断出不符合硬件描述的行为。本文单单聚焦于一些关于构造stimulus方面的想法吧&#xff0c;结合了红皮书, writing testbench和项目…

储殷黄日涵教授《丁香花》唐磊推荐杨语莲,意味拜师赵本山有望吗

熟悉娱乐圈的人都知道&#xff0c;这个圈子包含有很多潜规则&#xff0c;尤其是一些女艺人&#xff0c;想要有所成就&#xff0c;不是有才华就可以的。就拿音乐人杨语莲来说&#xff0c;她是一个非常有才华的歌手&#xff0c;然而就因为不接受潜规则&#xff0c;至今仍是歌红人…

设计模式:单例模式

1、单例模式 单例模式是指在整个系统生命周期内&#xff0c;保证一个类只能产生一个实例&#xff0c;确保该类的唯一性。 为什么需要单例模式 单例模式是为了保证程序的线程安全。 线程安全&#xff1a; 在拥有共享数据的多条线程并行执行的程序中&#xff0c;线程安全的代…

cuda和pytarch的安装-参考官网的安装-较为通用

文章目录cuda 安装PyTorch 1.x版本安装cuda 安装 官网&#xff1a;cuda各个版本安装教程 选择相应版本点击版本前方链接就可以进入安装教程页面 例如&#xff1a;我想要为ubuntu系统安装一个11.7版本的cuda&#xff0c;则选择11.7版本的连接&#xff0c;然后进入安装教程页面…

深度学习之优化算法

入门小菜鸟&#xff0c;希望像做笔记记录自己学的东西&#xff0c;也希望能帮助到同样入门的人&#xff0c;更希望大佬们帮忙纠错啦~侵权立删。 目录 一、优化算法与深度学习 1、优化算法对于深度学习的意义 2、优化算法与深度学习的关系 3、优化算法在深度学习中的主要挑…

如何用Spring整合MyBatis和Junit

Spring整合MyBatis和Junit一. 整合MyBatis1. 目录&#xff1a;2. pom.xml&#xff1a;3. domain层&#xff1a;4. dao层&#xff1a;5. service层&#xff1a;AccountService接口类&#xff1a;AccountServiceImpl实现类&#xff1a;6. jdbc.properties配置文件&#xff1a;7. …

HBase基于HDFS上是如何完成增删改查功能的

HDFS只支持文件append操作, 而依赖HDFS的HBase如何完成增删改查功能&#xff1f; 1.如何理解? 1.这句话有个更专业的说法&#xff1a;HDFS 采用数据流方式来访问文件&#xff0c;只支持单个客户端向一个文件追加数据. 2 上半句话&#xff0c;访问文件不外乎读和写&#xff0…