C++深入学习string类成员函数(1):默认与迭代

news2024/9/27 17:36:45

引言

在 C++ 编程中,std::string 类是处理字符串的核心工具之一。作为一个动态管理字符数组的类,它不仅提供了丰富的功能,还通过高效的内存管理和操作接口,极大地方便了字符串操作。通过深入探讨 std::string 的各类成员函数,我们将更加全面地理解它背后的机制和使用场景。

在本系列博客中,我们将详细剖析 std::string 的各类成员函数,带你深入理解 C++ 中字符串的操作与管理。以下内容均是在c++11标准版本的介绍。

1.默认成员函数

1.1string类的构造函数(Construct)

1.1.1函数原型

在C++中,std::string 是标准库提供的一个字符串类,它提供了多种构造函数来创建字符串对象。构造基本语法如下:

public member function
std::string::string

default (1)	       string();

copy (2)	       string (const string& str);

from c-string (3)  string (const char* s);

from sequence (4)  string (const char* s, size_t n);

fill (5)	       string (size_t n, char c);

substring (6)	   string (const string& str, size_t pos, size_t len = npos);

range (7)	       template <class InputIterator>
                      string  (InputIterator first, InputIterator last);

initializer list (8)	string (initializer_list<char> il);

move (9)	       string (string&& str) noexcept;

1.1.2 noexcept

noexcept是c++11引入的关键字,用来表明不会抛出异常,它在函数声明中使用,告诉编译器和程序员该函数在运行时不会引发异常。

(1)noexcept 的作用:

优化性能:编译器可以利用 noexcept 进行优化。如果编译器知道某个函数不会抛出异常,它可以避免生成一些与异常处理相关的代码,从而提高性能。

提高代码安全性:当你明确声明某个函数不会抛出异常,调用者可以放心地使用该函数,而不需要担心异常的传播和处理。

异常安全保障:当你编写不应该抛出异常的函数时,使用 noexcept 明确表达这种意图,防止由于代码错误导致异常意外抛出。

(2)使用场景

移动构造函数和移动赋值运算符:对于某些类型的对象,移动操作(如移动构造函数、移动赋值运算符)通常是 noexcept 的。这是因为在某些容器(如 std::vector)中,出于性能考虑,当元素在容器内部移动时,要求这些操作不会抛出异常。

异常安全的保证:如果你的函数不打算抛出异常,最好明确使用 noexcept 进行标记,提升代码的可读性和可维护性。

1.1.3深入解析

(1) 默认构造函数
创建一个空字符串对象。

std::string str;

此时,`str` 是一个空字符串。

(2)拷贝构造函数
通过另一个 `std::string` 对象来构造字符串。

 string (const string& str);

std::string str1 = "Hello";
std::string str2 = str1; // 使用str1来构造str2

(3)从 C 字符串构造
使用 C 风格的字符串(即字符数组)来构造 `std::string` 对象。

string (const char* s);

const char* cstr = "Hello";
std::string str(cstr);

(4)从 C 字符串的部分字符构造
可以指定从 C 字符串中提取的字符个数。

string (const char* s, size_t n);

const char* cstr = "Hello, World!";
std::string str(cstr, 5); // 只提取前5个字符,即"Hello"

(5)从字符构造
可以指定使用某个字符构造一个指定长度的字符串。

 string (size_t n, char c);

std::string str(5, 'A'); // 构造一个包含5个'A'字符的字符串 "AAAAA"

(6)从子字符串开始构造

 可以从现有的std::string对象中提取一个子字符串来构造新的std::string。pos是子字符串的起始位置,len表示要提取的字符数(默认为npos,表示从起始位置到字符串末尾)。

string (const string& str, size_t pos, size_t len = npos);  

    std::string original = "Hello, World!";
    // 从位置7开始,提取5个字符构造子字符串
    std::string subStr(original, 7, 5);// 输出 "World"

(7)从迭代器范围构造
可以使用迭代器范围来构造字符串,例如从数组或其他容器中提取内容。

 template <class InputIterator>
 string  (InputIterator first, InputIterator last);

std::vector<char> vec = {'H', 'e', 'l', 'l', 'o'};
std::string str(vec.begin(), vec.end()); // 构造 "Hello"

(8)初始化构造列表(C++11及更高版本)

std::string 的 initializer list 构造函数允许你使用一个 初始化列表(std::initializer_list<char>)直接初始化一个字符串对象。

string (initializer_list<char> il);

    std::string str = {'H', 'e', 'l', 'l', 'o'};
    
    std::cout << "str: " << str << std::endl;  // 输出 "Hello"

(9)移动构造函数 (C++11及更高版本)
可以利用移动语义,从一个临时的 `std::string` 对象中转移资源,而不需要复制。

string (initializer_list<char> il);

std::string str1 = "Hello";
std::string str2 = std::move(str1); // str2获得str1的资源,str1被置为空

总结
`std::string` 的构造方式灵活多样,可以从字符数组、部分字符、字符迭代器等多种来源构造。此外,在C++11及以上版本中,`std::string` 还支持移动构造,进一步优化了性能。

1.2赋值运算符重载(operator=)

1.2.1函数原型:

   string (1)         string& operator= (const string& str);
 c-string (2)	      string& operator= (const char* s);
character (3)	      string& operator= (char c);
initializer list (4)  string& operator= (initializer_list<char> il);
     move (5)	      string& operator= (string&& str) noexcept;

1.2.2深入解析 

(1)拷贝赋值运算符

拷贝赋值运算符用于将一个 `std::string` 对象赋值给另一个 `std::string` 对象。

string& operator= (const string& str);

    std::string str1 = "Hello";
    std::string str2;
    
    str2 = str1;  // 使用拷贝赋值运算符
    std::cout << "str1: " << str1 << std::endl;  // 输出 "Hello"
    std::cout << "str2: " << str2 << std::endl;  // 输出 "Hello"
    

(2)C 风格字符串赋值运算符

用于将从风格的字符串(字符数组指针)赋值给std::string对象

string& operator= (const char* s);

    const char* cStr = "Hello, World!";
    std::string str;
    
    str = cStr;  // 使用C风格字符串赋值运算符
    std::cout << "str: " << str << std::endl;  // 输出 "Hello, World!"

(3)字符赋值运算符

用于将一个字符赋值给 `std::string` 对象,将该字符作为字符串的唯一字符。

string& operator= (char c);

    char c = 'A';
    std::string str;
    
    str = c;  // 使用字符赋值运算符
    std::cout << "str: " << str << std::endl;  // 输出 "A"

(4)初始化列表赋值运算符

用于通过 C++11 提供的初始化列表,将多个字符赋值给 `std::string` 对象。

string& operator= (std::initializer_list<char> il);

    std::string str;
    
    str = {'H', 'e', 'l', 'l', 'o'};  // 使用初始化列表赋值运算符
    std::cout << "str: " << str << std::endl;  // 输出 "Hello"

(5)移动赋值运算符

用于将一个临时或即将被销毁的 `std::string` 对象的内容移动到当前对象,而不是复制。

string& operator= (string&& str) noexcept;

    string str1 = "hello";
	string str2;

	str2 = std::move(str1);
	cout << str1 << endl; // str1 可能为空或处于未定义状态
	cout << str2 << endl;// 输出 "Hello"

1.2.3总结

- `string& operator=(const string& str)`:将一个 `std::string` 对象赋值给另一个。
- `string& operator=(const char* s)`:将 C 风格字符串赋值给 `std::string`。
- `string& operator=(char c)`:将一个字符赋值给 `std::string`,该字符成为字符串的唯一字符。
- `string& operator=(initializer_list<char> il)`:使用初始化列表赋值多个字符。
- `string& operator=(string&& str) noexcept`:使用移动赋值运算符,将一个临时对象的内容移动到当前对象。

这些赋值运算符提供了不同的赋值方式,极大地增强了 `std::string` 的灵活性。

2.返回迭代器的成员函数

在 C++中,迭代器是一种对象,它能够用来遍历容器中的元素。迭代器提供了一种统一的方式来访问不同类型容器中的元素,而无需了解容器的内部实现细节。这里如果对迭代器不太了解,可以去看这篇博客C++标准库类——string类-CSDN博客。

std::string 提供了一系列返回迭代器的成员函数,用于遍历和操作字符串中的字符。通过这些迭代器,您可以轻松地访问、修改和遍历字符串中的每个字符。常见的迭代器包括正向迭代器、常量迭代器、反向迭代器以及它们的常量版本

2.1正向迭代器(iterator)

(1)begin()

- begin():返回指向字符串首个字符的迭代器。

      iterator begin() noexcept;
const_iterator begin() const noexcept;

 - iterator begin() noexcept 返回一个可修改的迭代器,指向字符串的第一个字符。

 - const_iterator begin() const noexcept 返回一个常量迭代器,指向字符串的第一个字符,且不允许通过该迭代器修改字符串内容。 

(2)end()

- end():返回指向字符串末尾(即最后一个字符的下一个位置)的迭代器

 iterator end() noexcept;
const_iterator end() const noexcept;

- iterator end()返回一个可修改的迭代器,指向字符串末尾的下一个位置。
- const_iterator end() const noexcept返回一个常量迭代器,指向末尾的下一个位置,且不允许通过该迭代器修改字符串内容

(3)示例

#include <iostream>
using namespace std;


int main()
{
	//使用非const迭代器遍历并修改字符
	string st1 = "Hello, henu";
	string::iterator it1 = st1.begin();
	while (it1 != st1.end())
	{
		if (*it1 == ',')
			*it1 = ';';
		it1++;
	}
	cout << st1 << endl;//Hello; henu

	//使用const迭代器遍历,但不能修改字符
	const string st2 = "Hello, world";
	string::const_iterator it2 = st2.begin();
	while (it2 != st2.end())
	{
		cout << *it2;//Hello, world
		it2++;
	}
   
    return 0;
}

(4)常量版本

- cbegin():返回指向字符串首个字符的常量迭代器。

const_iterator cbegin() const noexcept;

- cend():返回指向字符串末尾的常量迭代器。

const_iterator cend() const noexcept;

常量版本其实就是与上面返回常量迭代器的声明类型的作用是相同的,在此不再赘述。

2. 2反向迭代器(reverse_iterator)

(1)rbegin()

- rbegin():返回指向字符串末尾(即最后一个字符)的反向迭代器。

 reverse_iterator rbegin() noexcept;
const_reverse_iterator rbegin() const noexcept;

- reverse_iterator rbegin() 返回一个可修改的反向迭代器,指向字符串的最后一个字符。 

- const_reverse_iterator rbegin() const noexcept 返回一个常量反向迭代器,指向字符串的最后一个字符,且不允许通过该迭代器修改字符串内容

(2)rend()

- rend():返回指向字符串首个字符的前一个位置的反向迭代器。

 reverse_iterator rend() noexcept;
const_reverse_iterator rend() const noexcept;

- reverse_iterator rend() 返回一个可修改的反向迭代器,指向第一个元素之前的位置。

- const_reverse_iterator rend() const noexcept 返回一个常量反向迭代器,指向第一个元素之前的位置,且不允许通过该迭代器修改字符串内容。 

(3)示例

#include <iostream>
using namespace std;

int main()
{
	//使用 reverse_iterator遍历并修改字符
	string st1 = "Hello, world";

	//auto it1 = st1.rbegin();
	string::reverse_iterator it1 = st1.rbegin();
	while (it1 != st1.rend())
	{
		if (*it1 == ',')
			*it1 = ';';
		cout << *it1;
		it1++;//注意,这里用++,而不是--
	}
	cout << endl;

	//使用 const_reverse_iterator遍历并修改字符
	const string st2 = "Hello, henu";

	//auto it2 = st2.rbegin();
	string::const_reverse_iterator it2 = st2.rbegin();
	//string::const_reverse_iterator it2 = st2.crbegin();
	while (it2 != st2.rend())
	//while (it2 != st2.crend())
	{
		cout << *it2;
		it2++;
	}

	return 0;
}

(4)常量版本:

- crbegin():返回指向字符串末尾的常量反向迭代器。

const_reverse_iterator crbegin() const noexcept

- crend():返回指向字符串首个字符的前一个位置的常量反向迭代器。

const_reverse_iterator crend() const noexcept;

这里的常量版本同样与上面返回常量迭代器的声明的类型的作用相同,不再赘述。

2.3总结

- begin():正向迭代器,指向首个字符。
- end():正向迭代器,指向末尾后的位置。
- rbegin():反向迭代器,指向最后一个字符。
- rend():反向迭代器,指向第一个字符之前的位置。
- cbegin():常量正向迭代器,指向首个字符。
- cend():常量正向迭代器,指向末尾后的位置。
- crbegin():常量反向迭代器,指向最后一个字符。
- crend():常量反向迭代器,指向第一个字符之前的位置。 

更多string类的成员函数:string - C++ Reference (cplusplus.com)

 

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

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

相关文章

【java21】java21新特性之简单的Web服务器jwebserver

jwebserver是Java 18中引入的一个全新功能点&#xff0c;它允许用户通过命令行工具快速启动一个提供静态资源访问的迷你Web服务器。这个服务器不支持CGI和Servlet&#xff0c;因此其主要用途是轻量级的静态文件服务&#xff0c;如HTML、CSS、JavaScript和图片等。 其实在如Pyt…

热源检测系统源码分享

热源检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

大语言模型之LlaMA系列- LlaMA 2及LLaMA2_chat(上)

LlaMA 2是一个经过预训练与微调的基于自回归的transformer的LLMs&#xff0c;参数从7B至70B。同期推出的Llama 2-Chat是Llama 2专门为对话领域微调的模型。 在许多开放的基准测试中Llama 2-Chat优于其他开源的聊天模型&#xff0c;此外Llama 2-Chat还做了可用性与安全性评估。 …

大数据 flink 01 | 从零环境搭建 简单Demo 运行

什么是Flink Flink是一个开源的流处理和批处理框架,它能够处理无界和有界的数据流&#xff0c;具有高吞吐量、低延迟和容错性等特点 Flink 可以应用于多个领域如&#xff1a;实时数据处理、数据分析、机器学习、事件驱动等。 什么是流式处理&#xff1f;什么是批处理 流处理…

Python 如何使用 unittest 模块编写单元测试

Python 如何使用 unittest 模块编写单元测试 单元测试是软件开发过程中的重要环节&#xff0c;它帮助开发者验证代码的正确性&#xff0c;确保功能按预期工作。Python 提供了一个强大的内置模块 unittest&#xff0c;使得编写和执行单元测试变得非常方便。本文将深入探讨如何使…

计算机组成原理(笔记5原码和补码的乘法以及直接补码阵列乘法器 )

原码一位乘法 手算&#xff1a;过程 令x′|x|0.x1x2…xn-1xn&#xff0c;y′|y|0.y1y2…yn-1yn 同时令乘积P′ |P| x′ y′&#xff0c;有&#xff1a; x′ y′ x′(0.y1y2…yn-1yn) x′ (y12-1y22-2…yn-12-(n-1)yn2-n) 2-1(y1x′2-1(y2x′…2-1(yn-1x′2-1(ynx′0))…))…

使用awvs测试站点并输出漏洞报告教程

环境配置 pikachu靶场 awvs 使用步骤 1.访问本机3443端口&#xff08;安装时自己设定的端口&#xff09; 2.点击【Targets】--》【Add Targets】新建扫描目标&#xff0c;输入目标网址&#xff08;以pikachu靶场为例&#xff09;&#xff0c;点击【Save】开始扫描 2.点击【…

【AI大模型】股票价格预测精度增强,基于变分模态分解、PatchTST和自适应尺度加权层

简介 股票价格指数是金融市场和经济健康的晴雨表&#xff0c;准确预测对投资决策至关重要。股票市场的高频交易和复杂行为使得预测具有挑战性&#xff0c;需开发稳定、准确的预测模型。研究表明&#xff0c;估值比率、数据驱动模型&#xff08;如支持向量机&#xff09;、股票…

机器学习 | 使用scikit-learn学习Python中的PCA(主成分分析)

为什么选择PCA&#xff1f; 当有许多输入属性时&#xff0c;很难将数据可视化。在机器学习领域有一个非常著名的术语“维度诅咒”。基本上&#xff0c;它指的是数据集中的属性数量越多&#xff0c;对机器学习模型的准确性和训练时间产生不利影响。主成分分析&#xff08;PCA&a…

使用Postman工具接口测试

文章目录 一、接口1.1 接口的概念1.2 接口的类型 二、接口测试2.1 概念2.2 原理2.3 特点 三、HTTP协议3.1 http协议简介3.2 URL格式3.3 HTTP请求3.3.1 请求行3.3.2 请求头3.3.3 请求体 3.4 HTTP响应3.4.1 状态行3.4.2 响应头3.4.3 响应体 3.4 传统风格接口3.5 RESTful风格接口 …

二网络复习

软路由&#xff1a; 1. ikuai 实现了一个多宽带线路的一个聚合可用家庭环境 2. Linux通过开启路由转发模拟路由器 &#xff08;仅学习使用&#xff09; #开启路由转发命令 vim /etc/sysctl.conf net.ipv4.ip_forward 1 sys…

C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(1)

1、FRIENDS c允许类声明为其它类&#xff0c;其它类的成员函数&#xff0c;或者非成员函数为friend。可以访问protected与private数据成员与成员函数。例如&#xff0c;假设你有两个类Foo与Bar。你可以指定Bar类是Foo类的一个friend&#xff1a; class Foo {friend class Bar;…

《声入人心》团综重启,芒果能否再造一个群像神话?

随着《声入人心》团综《吾湖音乐局》于9月20日宣布重启&#xff0c;芒果的又一群像综艺“杀”回了市场。 从2018年音综市场冲出的一匹黑马&#xff0c;到2024年“声人”分散在影视综各个领域&#xff0c;这六年间芒果上演了无数次“狼来了”&#xff0c;但这一次团综是真的来了…

实现一个超轻量级实例分割网络的思路

文章目录 前言一、基本思路二、picodet三、yolact三、picodetyolact总结 前言 在某些工业领域&#xff0c;由于成本问题算力有限&#xff0c;只能实时跑一些超轻量级网络&#xff0c;拿目标检测来说&#xff0c;例如yolo-fast&#xff0c;pp-picodet这些。如果要跑实例分割&am…

魅思-视频管理系统 getOrderStatus SQL注入漏洞复现

0x01 产品简介 魅思-视频管理系统是一款集成了视频管理、用户管理、手机端应用封装等功能的综合性视频管理系统。该系统不仅以其强大的视频管理功能、灵活的用户管理机制、便捷的手机端应用封装功能以及高安全性和现代化的界面设计,成为了市场上备受关注的视频管理系统之一。…

分布式数据库——HBase基本操作

启动HBase: 1.启动hadoop,进入hadoop的sbin中 cd /opt/hadoop/sbin/ 2.初始化namenode hdfs namenode -format 3.启动hdfs ./start-all.sh 4.启动hbase cd /opt/hbase/bin ./start-hbase.sh 5.使用jps查看进程 jps 以下图片则是hbase启动成功~ 运行HBase ./hbase sh…

软考中项第3版新教程变化,2张表格看到底

近期正在着手做《信息系统项目管理师一站通关》书友会的见面礼包时&#xff0c;无意中在电脑中翻到不知道什么时候保存的2张表格&#xff0c;出处已经记不得了&#xff0c;这2张表格对软考中项第3版新教程的变化点总结的言简意赅&#xff0c;有价值分享出来给你。 第1张表格如下…

使用C计算数码管段码

前言 平时使用数码管时为了避免使用跳线往往不会按照顺序焊接数码管的段选引脚&#xff0c;为了焊接的方便段选引脚可能会焊接的乱七八糟的&#xff0c;此时标准的段码表就用不了了&#xff0c;需要重新去计算。因为在焊接时为了考虑布线可能每次焊的顺序都会有不同&#xff0c…

使用vite+react+ts+Ant Design开发后台管理项目(三)

前言 本文将引导开发者从零基础开始&#xff0c;运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈&#xff0c;构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导&#xff0c;文章旨在为开发者揭示如何利用这些技术…

使用AI进行需求分析的案例研究

生成式 AI 的潜在应用场景似乎无穷无尽。虽然这令人兴奋&#xff0c;但也可能让人不知所措。因此&#xff0c;团队在使用这项技术时需要有明确的目标&#xff1a;关键是要明确生成式 AI 在团队工作中能产生哪些实质性影响。 在软件工程中&#xff0c;一个引人注目的应用场景是…