C++STL~~string

news2024/9/24 21:26:47

文章目录

    • 一、string类的发展历史
    • 二、string的使用
    • 三、string的练习
    • 四、总结

一、string类的发展历史

在C++ 的早期版本中,处理字符串主要依赖于 C 风格的字符数组。但这种方式存在诸多不便,如手动管理内存、容易出现缓冲区溢出等问题。随着 C++ 标准的不断演进,string 类逐渐成为处理字符串的更安全和便捷的方式。
在 C++98 标准中,引入了 std::string 类,它提供了自动内存管理、丰富的操作方法,如字符串连接、查找、替换等。这使得字符串的操作更加直观和安全。例如,在 C 风格中连接两个字符串可能需要手动分配足够的内存并复制字符,而使用 std::string 可以简单地通过 + 运算符实现。
C++11 标准进一步增强了 string 的功能,例如提供了更高效的移动语义,使得字符串的传递和赋值更加高效。
C++17 及之后的标准也对 string 的性能和功能进行了一些优化和改进。
总的来说,C++ 中 string 的发展历史是一个不断改进和完善的过程,旨在为开发者提供更强大、更安全和更高效的字符串处理工具。

二、string的使用

(1).string类对象的常见构造

在这里插入图片描述
在这里插入图片描述

void Teststring()
{
  string s1; // 构造空的string类对象s1
  string s2("hello Tu"); // 用C格式字符串构造string类对象s2
  string s3(s2); // 拷贝构造s3
}

(2).string类对象的容量操作
在这里插入图片描述

void Teststring1()
{
	// 注意:string类对象支持直接用cin和cout进行输入和输出
	string s("hello, TU!!!");
	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;
}
  1. size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一
    致,一般情况下基本都是用size()。

  2. clear()只是将string中有效字符清空,不改变底层空间大小。

  3. resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字
    符个数增多时:resize(n)用0来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的
    元素空间。注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大
    小,如果是将元素个数减少,底层空间总大小不变。

  4. reserve(size_t res_arg=0):为string预留空间,不改变有效元素个数,当reserve的参数小于
    string的底层空间总大小时,reserver不会改变容量大小。

(3)string类对象的访问及遍历操作
在这里插入图片描述

void Teststring2()
{
	string s("hello TU");
	// 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;
}

(4)string类对象的修改操作
在这里插入图片描述

// 测试string:
// 1. 插入(拼接)方式:push_back  append  operator+= 
// 2. 正向和反向查找:find() + rfind()
// 3. 截取子串:substr()
// 4. 删除:erase
void Teststring3()
{
	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;
}
  1. 在string尾部追加字符时,s.push_back© / s.append(1, c) / s += 'c’三种的实现方式差不多,一般
    情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
  2. 对string操作时,如果能够大概预估到放多少字符,可以先通过reserve把空间预留好。

(5) string类非成员函数
在这里插入图片描述
getline 函数的一般使用形式

#include <iostream>
#include <string>

int main() {
    std::string str;
    std::cout << "请输入一行字符串: ";
    std::getline(std::cin, str);
    std::cout << "您输入的字符串是: " << str << std::endl;
    return 0;
}

在上述代码中,std::getline(std::cin, str) 表示从标准输入中读取一行内容,并将其存储到 str 字符串中。
getline 函数的一个重要特点是它可以读取包含空格的字符串,而不像 cin >> str 那样在遇到空格时就停止读取。
例如,如果用户输入 “Hello World” ,使用 cin >> str 时,str 只会被赋值为 “Hello” ,而使用 getline 则可以完整地将 “Hello World” 存储到 str 中。
relational operators函数的一般使用形式
在这里插入图片描述

#include <iostream>
#include <vector>
#include <string>

int main ()
{
  std::string foo = "alpha";
  std::string bar = "beta";

  if (foo==bar) std::cout << "foo and bar are equal\n";
  if (foo!=bar) std::cout << "foo and bar are not equal\n";
  if (foo< bar) std::cout << "foo is less than bar\n";
  if (foo> bar) std::cout << "foo is greater than bar\n";
  if (foo<=bar) std::cout << "foo is less than or equal to bar\n";
  if (foo>=bar) std::cout << "foo is greater than or equal to bar\n";

  return 0;
}

这段 C++ 代码主要是对两个字符串 foo(值为 “alpha”)和 bar(值为 “beta”)进行关系比较,并根据比较结果输出相应的信息。
由于 “alpha” 和 “beta” 在字典序上不同,且 “alpha” 小于 “beta”,所以输出结果为:

foo and bar are not equal
foo is less than bar
foo is less than or equal to bar

(6)vs和g++下string结构的说明
1.vs下string的结构
string总共占28个字节,内部结构稍微复杂一点,先是有一个联合体,联合体用来定义string中字符串的存储空间:

  • 当字符串长度小于16时,使用内部固定的字符数组来存放
  • 当字符串长度大于等于16时,从堆上开辟空间
union _Bxty
{ // storage for small buffer or pointer to larger one
 value_type _Buf[_BUF_SIZE];
 pointer _Ptr;
 char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;

这种设计也是有一定道理的,大多数情况下字符串的长度都小于16,那string对象创建好之后,内
部已经有了16个字符数组的固定空间,不需要通过堆创建,效率高。

  • 其次:还有一个size_t字段保存字符串长度,一个size_t字段保存从堆上开辟空间总的容量
  • 最后:还有一个指针做一些其他事情。

故总共占16+4+4+4=28个字节。
在这里插入图片描述

2.g++下string的结构
G++下,string是通过写时拷贝实现的,string对象总共占4个字节,内部只包含了一个指针,该指
针将来指向一块堆空间,内部包含了如下字段:

  • 空间总大小
  • 字符串有效长度
  • 引用计数
struct _Rep_base
{
 size_type _M_length;
 size_type _M_capacity;
 _Atomic_word _M_refcount;
};
  • 指向堆空间的指针,用来存储字符串。

三、string的练习

1.反转字符串
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

我们使用两个指针,一个指向字符串的开头 left,一个指向字符串的末尾 right。然后通过一个循环,不断交换这两个指针所指向的字符,直到两个指针相遇或者交错。这样就实现了字符串的反转,而且没有使用额外的存储空间,只在原字符串上进行操作,空间复杂度为 O(1)。

class Solution {
public:
    void reverseString(vector<char>& s) {
        
        int left=0,ringt=s.size()-1;
        while(left<ringt)
        {
            swap(s[left++],s[ringt--]);
        }
    }
};

2.字符串相加
给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

思路如下:
我们从两个字符串的末尾开始逐位相加。使用两个指针 i 和 j 分别指向 num1 和 num2 的最后一位。同时设置一个进位 carry 初始化为 0 。
在每次循环中,如果指针还没有超出字符串范围,就取出当前位的数字进行相加,再加上进位。计算出当前位的和后,得到新的进位,并将当前位的数字(取模 10 )添加到结果字符串中。
如果指针超出了字符串范围,就认为当前位为 0 。
最后,因为我们是从低位向高位计算并添加到结果字符串的,所以需要将结果字符串反转,得到最终正确的结果。

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1=num1.size()-1,end2=num2.size()-1;
        int next =0;
        string str;
        while(end1>=0||end2>=0)
        {
            int val1= end1 >= 0 ? num1[end1--]-'0':0;
            int val2= end2 >= 0 ? num2[end2--]-'0':0;
            int sum =val1+val2+next;
            next = sum / 10;
            sum = sum % 10;
            str.insert(str.begin(),'0'+sum);
        }
        if(next==1)
        {
            str.insert(str.begin(),'1');
        }
        return str;
    }
};

3.字符串中的第一个唯一字符
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。

1.int cont[26]={0};:创建了一个大小为 26 的整数数组 cont,用于统计每个小写字母出现的次数,并初始化为 0。
2.for(auto ch :s):这是一个范围 for 循环,遍历字符串 s 中的每个字符 ch。
cont[ch-‘a’]++;:通过 ch - ‘a’ 将字符转换为对应的索引(例如,‘a’ - ‘a’ = 0,‘b’ - ‘a’ = 1 等),然后将对应索引位置的计数加 1,从而统计每个字符出现的次数。
3.第二个 for 循环:再次遍历字符串 s。
4.if(cont[s[i]-‘a’]==1):对于当前位置的字符,通过同样的方式将其转换为索引,然后检查其在 cont 数组中的计数是否为 1。如果是,则返回当前位置的索引 i。
如果整个字符串遍历完都没有找到计数为 1 的字符,就返回 -1。
这种方法的时间复杂度为 O(n),空间复杂度为 O(26),主要是通过一次遍历统计字符出现次数,再一次遍历查找第一个不重复的字符,避免了使用复杂的数据结构,提高了效率。

class Solution {
public:
    int firstUniqChar(string s) {
        int cont[26]={0};
        for(auto ch :s)
        {
            cont[ch-'a']++;             
        }
        for(size_t i=0;i<s.size();i++)
        {
            if(cont[s[i]-'a']==1)
            {
                return i;
            }
            
        }
        return -1;
    }
};

四、总结

std::string 是 C++ 标准库中用于处理字符串的重要类。
内存管理:
自动管理内存,无需手动分配和释放,避免了内存泄漏和缓冲区溢出的风险。动态调整字符串的存储空间,以适应字符串内容的变化。
丰富的操作:
支持字符串的连接,通过 + 运算符可以方便地将两个字符串拼接在一起。提供查找、替换、比较等操作,如 find() 、 replace() 、 compare() 。可以获取字符串的长度、子串等。
高效性:
在 C++11 及以后的标准中,利用移动语义提高了字符串对象的传递和赋值效率。
迭代访问:可以使用迭代器遍历字符串中的每个字符。

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

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

相关文章

使用Linux实现FTP云盘1

关于FTP服务器 FTP&#xff08;文件传输协议&#xff09;服务器是在互联网上提供文件存储和访问服务的计算机&#xff0c;它们依照FTP 协议提供服务。 FTP是File Transfer Protocol(文件传输协议)。 程序运行&#xff0c;服务端不断接收客户端指令&#xff0c;服务 端可同时处…

一文概叙自制舵机云台

本文主要涉及选择合适的舵机、设计云台结构、编写控制代码以及组装调试等步骤。以下是一个详细的制作流程&#xff1a; 一、材料准备 1、舵机&#xff1a; 通常需要至少两个舵机&#xff0c;一个用于控制云台的左右旋转&#xff0c;另一个用于控制云台的上下倾斜。先以简单的…

渲染引擎实践 - UnrealEngine引擎 GLContext 创建过程

一:概述: 本文分析下 UnrealEngine 启动过程中创建多少个 OpenGL Context,以及这些 Context 的作用。 二:临时Context 1. PreInit -> PreInitPreStartupScreen -> PreloadResolutionSettings, 用于检查图形窗口分辨率 2. PreInit -> PreInitPreStartupScreen -&…

高效清理优化工具 Sonoma Cache Cleaner mac 19.0.6注册激活版

Sonoma Cache Cleaner 是一款专为 Mac 系统设计的强大清理优化工具。它能够深度扫描系统&#xff0c;清理各类缓存文件&#xff0c;释放宝贵的存储空间。不仅如此&#xff0c;还能优化系统性能&#xff0c;让您的 Mac 运行更加流畅快捷。无论是系统日志、临时文件还是浏览器缓存…

ArcGIS基础:以分数形式进行标注字段

分数形式标注在项目或者工作中可能会用到 基于VBScript进行分式标注的通用形式为&#xff1a; "<und>"&""& 分子字段&""&"</und>"&vbNewLine& 分母字段按下述顺序进行操作标注 "<und…

VScode:前端项目中导出和导入插件

# 终端运行&#xff1a;导出扩展插件到指定路径&#xff08;txt&#xff09; code --list-extensions > C:\Users\UserName\Documents\extensions.txt # 终端运行&#xff1a;导入指定路径&#xff08;txt&#xff09;的扩展插件 Get-Content C:\Users\UserName\Documen…

【独家原创区间概率预测】CNN-BiLSTM-SEAttention-ABKDE多变量时序预测-区间预测

【独家原创区间概率预测】CNN-BiLSTM-SEAttention-ABKDE多变量时序预测-区间预测 基于卷积神经网络(CNN)结合双向长短期记忆网络(BiLSTM)结合SE注意力机制并结合自适应带宽核函数密度估计的多变量时序预测【点预测概率预测核密度估计】 程序已调试好&#xff0c;无需更改代码&a…

Leetcode每日刷题之面试题01.01.判断字符是否唯一

在学习编程语言的过程中相信大部分同学刚开始接触的循环语句都是 for 循环&#xff0c;今天我将介绍一个比较简洁的循环语句&#xff0c;可以帮助我们减少些许的代码量&#xff0c;也更加快捷&#xff0c;那就是范围 for 在我之前的博文中也有所介绍 详情点击&#xff1a;面向对…

React-Lines-Ellipsis:插件处理多行文本截断

实现自适应的多行文本截断并添加省略号。该项目依赖于CSS Flexbox布局&#xff0c;确保在各种屏幕尺寸和设备上都能正常工作&#xff0c;无需手动计算高度 安装 法1&#xff1a;使用插件react-lines-ellipsis &#xff08;适用范围&#xff1a;使用react的项目&#xff09; npm…

JarEditor:一款直接编辑修改 jar 包内文件IDEA 插件【送源码】

作为一名 Java 程序员&#xff0c;在维护一些古老的程序时&#xff0c;可能会遇到这种情况&#xff1a;项目依赖的 jar 包过于久远&#xff0c;已经没有源码了&#xff0c;但是有不得不修改的 bug 要处理。这时候就得想办法反编译 jar 包进行修改&#xff0c;并且重新打包&…

LeetCode 热题 HOT 100 (019/100)【宇宙最简单版】

【链表】No. 0142 环形链表 II【中等】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#…

Redis数据失效监听

一、配置Redis开启 打开conf/redis.conf 文件&#xff0c;添加参数&#xff1a;notify-keyspace-events Ex 二、验证配置 步骤一&#xff1a;进入redis客户端&#xff1a;redis-cli步骤二&#xff1a;执行 CONFIG GET notify-keyspace-events &#xff0c;如果有返回值证明配…

初始化列表的基本介绍

为了树立初始化列表&#xff0c;我们先引进有参构造函数来理解 在上图的有参构造函数中我们可以将其转换为初始化列表&#xff0c;初始化列表有两种方式&#xff0c;一种是有参一种是无参&#xff0c;一会我会分别举例子&#xff0c;其语法为类名():属性&#xff08;值&#xf…

ffmpeg: 将flv格式的视频推流时报错: Failed to update header with correct duration

问题描述 我在将flv格式的视频推给rtmp服务器的时候&#xff0c;报错Failed to update header with correct duration&#xff0c;截图如下&#xff1a; 我的推流命令是 ffmpeg -stream_loop -1 -re -i wait-voice.flv -c:a copy -c:v copy -f flv rtmp://192.18.1.29:1935/…

C++入门知识点总结(下篇·初学必看)

前言&#xff1a;Hello大家好&#x1f618;&#xff0c;我是心跳sy&#xff0c;这篇文章将介绍下篇知识点内容&#xff0c;本系列文章将会更新关于C的全部初阶以及进阶的知识点&#xff0c;喜欢的小伙伴点个关注不迷路哦~我们一起来看看吧~ 目录 一、引用 &#x1f4ab; 1.1…

SAP 如何通过程序创建一个请求

一&#xff1a;首先我们需要知道SAP的资源类型 PGMID &#xff1a;程序ID OBJECT &#xff1a;对象类型 OBJ_NAME&#xff1a;对象名称 SY-REPID 我们可以通过SE09/SE10来创建一个请求号也能看的出来 也可以通过SE03来查找 请求号相关的数据都在E07*的表里这边…

IP 地址分类

IP 地址有两个版本&#xff1a; IPv4 和 IPv6 &#xff0c;目前使用最广泛的还是 IPv4 &#xff0c; IPv6 出现的目的主要是解决 IPv4 地址耗尽的问题。为了方便起见&#xff0c;在这里我们说的 IP 地址主要是指 IPv4 地址。 IP 地址是由 32 位比特组成&#xff0c;比如这个地…

初步认识二叉树

二叉树的概念及结构 二叉树在我们的想象中长这样 下图是满二叉树 二叉树有左右子树 1是根结点&#xff0c; 1的左子树是2&#xff0c;右子树是3&#xff1b; 2是根结点&#xff0c; 2的左子树是4&#xff0c;右子树是5&#xff1b; 3是根结点&#xff0c; 3的左子树是6…

odoo17 精减设置模块

odoo17 精减设置模块 设置模块中好多平时用不到的功能&#xff0c;如何隐藏&#xff0c;采取了继承修改方式&#xff0c;代码如下: <?xml version"1.0" encoding"utf-8" ?> <odoo><record id"resconfig_ex_form" model"i…

《人工智能商》:谁掌握了人工智能时代的思考方式,谁就掌握了未来

文/石墨杨&#xff08;shimoyang11&#xff09; 在可以预见的未来&#xff0c;马路上行驶着自动驾驶的汽车&#xff0c;工厂里忙碌着不知疲倦的机器人&#xff0c;网络电商会精准推荐你需要的商品&#xff0c;社交网站自动帮你识别令你心仪的朋友…… 《人工智能商》一书&#…