C++ STL之string的使用及模拟实现

文章目录

  • 1. 前言
  • 2. 介绍
  • 3. string类的使用
    • 3.1 string类的构造函数
    • 3.2 string类对象的容量操作
    • 3.3 string类对象的访问及遍历操作
    • 3.4 string类对象的修改操作
    • 3.5 string类对象的字符串操作
    • 3.6 string类的非成员函数
  • 4. string类的模拟实现


1. 前言

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合面向对象编程的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。所以C++引入了string类作为标准库中用于处理字符串的数据类型。string类封装了字符串的操作,提供了更方便、安全和高级的字符串处理功能,更符合面向对象编程的思想。

2. 介绍

英文解释:

在这里插入图片描述

简单来说:

  1. string是表示字符串的字符串类
  2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。
  3. string在底层实际是:basic_string模板类的别名,typedef basic_string<char, char_traits, allocator>string;
  4. 不能操作多字节或者变长字符的序列。

3. string类的使用

3.1 string类的构造函数

(constructor)函数代码功能说明
string();(默认构造函数)构造一个空字符串,长度为零个字符。
string(const string& str);(拷贝构造函数)构造一个 str 的副本。
string (const string& str, size_t pos, size_t len = npos);(子串构造函数)拷贝 str 从字符位置 pos 开始,跨越 len 个字符(如果 str 太短或者 len 为string::npos,则复制直到 str 的末尾)。
string(const char* s);拷贝由 s 指向的以空字符结尾的字符序列(C 字符串)。
string (const char* s, size_t n);从由 s 指向的字符数组中拷贝前 n 个字符。
string(size_t n, char c);使用字符 c 连续填充字符串 n 次。
template <class InputIterator> string (InputIterator first, InputIterator last);拷贝迭代器区间[first, last)范围内的字符序列。

实例:

// string constructor
#include <iostream>
#include <string>

int main()
{
	std::string s0("Initial string");

	// 按照上述描述的顺序使用的构造函数
	std::string s1;
	std::string s2(s0);
	std::string s3(s0, 8, 3);
	std::string s4("A character sequence");
	std::string s5("Another character sequence", 12);
	std::string s6a(10, 'x');
	std::string s6b(10, 42);      // 42 is the ASCII code for '*'
	std::string s7(s0.begin(), s0.begin() + 7);

	std::cout << "s1: " << s1 << "\ns2: " << s2 << "\ns3: " << s3;
	std::cout << "\ns4: " << s4 << "\ns5: " << s5 << "\ns6a: " << s6a;
	std::cout << "\ns6b: " << s6b << "\ns7: " << s7 << '\n';
	return 0;
}

输出结果:

在这里插入图片描述

3.2 string类对象的容量操作

函数名称代码功能说明
sizesize_t size() const;返回字符串有效字符长度。
lengthsize_t length() const;返回字符串有效字符长度。
max_sizesize_t max_size() const;返回字符串可以达到的最大长度。
resizevoid resize (size_t n);
void resize (size_t n, char c);
将字符串调整为长度为 n 的字符。如果 n 小于当前字符串的长度,当前值将缩短为其前 n 个字符,删除第 n 个字符之后的字符。如果 n 大于当前字符串的长度,当前内容将被扩展,插入足够数量的字符以达到大小为 n。如果指定了字符 c,则新元素将被初始化为 c 的副本,否则它们将是值初始化的字符(空字符)。
capacitysize_t capacity() const;以字节为单位,返回当前为字符串分配的存储空间的大小。
reservevoid reserve (size_t n = 0);请求将字符串的容量调整为计划更改的大小,最多达到长度为 n 的字符。如果 n 大于当前字符串的容量,该函数将导致容器增加其容量以容纳 n 个字符(或更多)。在所有其他情况下,它被视为非强制性请求来缩小字符串的容量:容器的实现可以自由优化,并保留容量大于 n 的字符串。该函数不会对字符串长度产生影响,也不能修改其内容。
clearvoid clear();清空有效字符。
emptybool empty() const;返回字符串是否为空(即长度是否为 0)。

3.3 string类对象的访问及遍历操作

函数名称代码功能说明
operator[]char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
返回字符串中位置 pos 处字符的引用。
atchar& at (size_t pos);
const char& at (size_t pos) const;
返回字符串中位置 pos 处字符的引用。该函数会自动检查 pos 是否是字符串中一个有效的字符位置(即,pos 是否小于字符串的长度),如果不是,则抛出 out_of_range 异常。
backchar& back();
const char& back() const;
返回字符串的最后一个字符的引用。该函数不应在空字符串上调用。
frontchar& front();
const char& front() const;
返回字符串的第一个字符的引用。

遍历操作

#include <iostream>
#include <string>

int main()
{
	std::string str("Test string");
	// 1.普通下标遍历
	for (size_t i = 0; i < str.size(); ++i)
		std::cout << str[i];
	std::cout << '\n';

	// 2.迭代器遍历
	for (std::string::iterator it = str.begin(); it != str.end(); ++it)
		std::cout << *it;
	std::cout << '\n';

	// 3.范围for遍历
	for (auto e : str)
		std::cout << e;
	std::cout << '\n';

	return 0;
}

解释

  1. 普通下标遍历:
    在这个部分的代码中,使用了一个普通的for循环和下标操作符[]来遍历字符串中的字符。循环变量i从0开始递增,直到达到字符串的长度(通过str.size()获取)。在每次迭代中,使用str[i]来获取对应位置的字符,并使用std::cout打印出来。
  2. 迭代器遍历:
    这部分代码使用了迭代器来遍历字符串中的字符。通过调用str.begin()获取字符串的起始位置迭代器,并使用str.end()获取字符串的结束位置迭代器。然后,使用一个for循环和迭代器操作来遍历字符串中的每个字符。在每次迭代中,使用解引用操作符*来获取当前迭代器指向的字符,并使用std::cout打印出来。
  3. 范围for遍历:
    在这部分代码中,使用了范围for循环来遍历字符串中的字符。通过for (auto e : str)的语法,将字符串中的每个字符依次赋值给变量e。在每次迭代中,直接使用变量e来表示当前字符,并使用std::cout打印出来。

3.4 string类对象的修改操作

函数名称代码功能说明
operator+=string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
在字符串末尾追加字符串 str、C字符串 s 或字符 c。
appendstring& append (const string& str);在字符串后追加一个字符串 str。
push_backvoid push_back (char c);在字符串后尾插字符 c。
insertstring& insert (size_t pos, const string& str);在指定位置 pos 插入另一个字符串 str。
erasestring& erase (size_t pos = 0, size_t len = npos);从字符串的位置 pos 开始删除长度为 len 的字符(如果内容太短或者 len 为 string::npos,则直到字符串的末尾)。如果未提供 len 参数,则默认删除从位置 pos 开始到字符串末尾的所有字符(类似于成员函数 clear)。返回一个引用,指向当前字符串的修改后的对象。

3.5 string类对象的字符串操作

函数名称代码功能说明
c_strconst char* c_str() const;回一个指向以空字符结尾的字符序列(即 C 字符串)的指针,该字符序列表示当前字符串对象的值。
substrstring substr (size_t pos = 0, size_t len = npos) const;返回一个新构造的字符串对象,其值为当前对象的子串副本。子串是从字符位置 pos 开始的、长度为 len 的字符序列(或者直到字符串的末尾)。
findsize_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
在当前字符串的 pos 索引位置(默认为0)开始,查找子串 str(或C 字符串 s),返回找到的位置索引,-1表示查找不到子串。
rfindsize_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
在当前字符串的 pos 索引位置开始,反向查找子串 str(或C 字符串 s),返回找到的位置索引,-1表示查找不到子串。
find_first_ofsize_t find_first_of (const char* s, size_t pos = 0) const;在当前字符串的 pos 索引位置(默认为0)开始,在字符串中搜索与其参数中指定的任意字符匹配的第一个字符。
find_last_ofsize_t find_last_of (const char* s, size_t pos = npos) const;在当前字符串的 pos 索引位置(默认为0)开始,在字符串中搜索与其参数中指定的任意字符匹配的最后一个字符。
find_first_not_ofsize_t find_first_not_of (const char* s, size_t pos = 0) const;在当前字符串的 pos 索引位置开始,在字符串中搜索与其参数中指定的任意字符不匹配的第一个字符。
find_last_not_ofsize_t find_last_not_of (const char* s, size_t pos = npos) const;在当前字符串的 pos 索引位置开始,在字符串中搜索与其参数中指定的任意字符不匹配的最后一个字符。

3.6 string类的非成员函数

函数名称代码功能说明
operator+string operator+ (const string& lhs, const string& rhs);返回一个新构造的字符串对象,其值是将 lhs 的字符后跟 rhs 的字符连接而成。
operator>>istream& operator>> (istream& is, string& str);支持 cin 读入 string 类对象,遇到空格回车结束。
operator<<ostream& operator<< (ostream& os, const string& str);支持 cout 输出 string 类对象。
getlineistream& getline (istream& is, string& str);从输入流 is 中提取字符,并将它们存储到字符串 str 中,直到找到换行字符 ‘\n’。
relational operatorsoperator==
operator!=
operator<
operator<=
operator>
operator>=
支持比较操作符(==,!=,<,<=,>,>=)

说明

  • getline(cin, s)会获取前一个输入的换行符,需要在前面添加读取换行符的语句。如:getchar() cin.get()

    例如:

    int n;
    string s;
    cin >> n;
    getchar(); //cin.get()
    getline(cin, s); // 可正确读入下一行的输入
    
  • operator+尽量少用,因为传值返回,导致深拷贝效率低。

4. string类的模拟实现

// string.h 文件
#pragma once
#include <iostream>
#include <string.h>
#include <assert.h>
using namespace std;

namespace bit
{
	class string
	{
		friend ostream& operator<<(ostream& _cout, const bit::string& s);
		friend istream& operator>>(istream& _cin, bit::string& s);
        
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
        
	public:
		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);
		}
        
		string(const string& s)
			:_size(0)
			, _capacity(0)
			, _str(nullptr)
		{
			string tmp(s._str);
			swap(tmp);
		}
        
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
			}
			return *this;
		}
        
		~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}
        
		iterator begin()
		{
			return _str;
		}
        
		iterator end()
		{
			return _str + _size;
		}
        
		const_iterator begin() const
		{
			return _str;
		}
        
		const_iterator end() const
		{
			return _str + _size;
		}
        
		size_t size()const
		{
			return _size;
		}
        
		size_t capacity()const
		{
			return _capacity;
		}
        
		bool empty()const
		{
			return _size;
		}
        
		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 c)
		{
			if (n <= _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				while (_size < n)
				{
					_str[_size] = c;
					++_size;
				}
				_str[_size] = '\0';
			}
		}
        
		void push_back(char c)
		{
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			_str[_size++] = c;
			_str[_size] = '\0';
		}
        
		string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
        
		void append(const char* str)
		{
			size_t len = strlen(str);
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
            
			strcpy(_str + _size, str);
			_size += len;
		}
        
		string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
        
		string substr(size_t pos, size_t len)
		{
			string s;
			size_t end = pos + len - 1;
			if (len == npos || pos + len >= _size)
			{
				len = _size - pos;
				end = _size;
			}
            
			s.reserve(len);
			for (size_t i = pos; i <= end; i++)
			{
				s += _str[i];
			}
            
			return s;
		}
        
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}
        
		const char* c_str()const
		{
			return _str;
		}
        
		char& operator[](size_t index)
		{
			assert(index < _size);
			return _str[index];
		}
        
		const char& operator[](size_t index)const
		{
			assert(index < _size);
			return _str[index];
		}
        
		bool operator<(const string& s)
		{
			return strcmp(_str, s._str) < 0;
		}
        
		bool operator==(const string& s)
		{
			return strcmp(_str, s._str) == 0;
		}
        
		bool operator<=(const string& s)
		{
			return *this < s || *this == s;
		}
        
		bool operator>(const string& s)
		{
			return !(*this <= s);
		}
        
		bool operator>=(const string& s)
		{
			return !(*this < s);
		}
        
		bool operator!=(const string& s)
		{
			return !(*this == s);
		}
        
		size_t find(char c, size_t pos) const
		{
			while (pos < _size)
			{
				if (_str[pos] == c)
				{
					return pos;
				}
				pos++;
			}
			return npos;
		}
        
		size_t find(const char* s, size_t pos) const
		{
			const char* p = strstr(_str + pos, s);
			if (p)
				return p - _str;
			else
				return npos;
		}
        
		bit::string& insert(size_t pos, char c)
		{
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : 2 * _capacity);
			}
			size_t end = _size + 1;
			while (end >= pos + 1)
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = c;
			_size++;
			return *this;
		}
        
		bit::string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (len == 0)
				return *this;
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}
			size_t end = _size + len;
			while (end >= pos + len)
			{
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
		}
        
		bit::string& erase(size_t pos, size_t len)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					++begin;
				}
				_size -= len;
			}
			return *this;
		}
		
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
        
	public:
		const static size_t npos;
	};
	ostream& operator<<(ostream& _cout, const bit::string& s)
	{
		for (auto e : s)
			_cout << e;
		return _cout;
	}
    
	istream& operator>>(istream& _cin, bit::string& s)
	{
		s.clear();
		char buff[129];
		size_t i = 0;
		char ch;
		ch = _cin.get();
        
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 128)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
            
			ch = _cin.get();
		}
        
		if (i != 0)
		{
			buff[i] = '\0';
			s += buff;
		}
        
		return _cin;
	}
};

const size_t bit::string::npos = -1;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Ubuntu / Raspbian 上安装 MariaDB

Raspberry Pi OS&#xff08;原为Raspbian&#xff09;是为树莓派基于Debian开发的操作系统。 从2015年起&#xff0c;树莓派基金会正式将其作为树莓派的官方操作系统。 Raspbian是由Mike Thompson和Peter Green创建的一个独立项目。第一个版本于2012年6月发布&#xff0c;至…

php基础学习之变量

php使用变量的必要性 PHP 是一种动态网站开发的脚本语言&#xff0c;动态语言特点是交互性&#xff0c;会有数据的传递&#xff0c;而 PHP作为“中间人”&#xff0c;需要进行数据的传递&#xff0c;传递的前提就是 PHP 能自己存储数据(临时存储) php变量的命名规则 必须以do…

Android Studio 之 菜单 Menu

选项菜单 OptionsMenu 用xml添加&#xff08;更建议使用&#xff09; 创建一个菜单布局 : 在 res文件下新建一个menu 目录&#xff0c;此时的菜单id为&#xff1a;R.menu.option <?xml version"1.0" encoding"utf-8"?> <menu xmlns:android&…

从一到无穷大 #21 从基于多数据模型分析负载的Benchmark讨论多模数据库的发展方向

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 文章目录 引言M2Bench测试结果从Lindorm看待多模的发展方向总结 引言 《M2Bench: A Database …

Java 面向对象 03 就近原则和this关键字

对于起名字需要见名知意&#xff0c;所以这个String n 不太合适&#xff1a; 但是如果将n改为name&#xff0c;会与第五行代码的name重复&#xff1a; 运行代码发现&#xff0c;获取后的姓名为默认值&#xff0c;是null 引入就近原则&#xff1a; 此处打印的是age10&#xff0c…

CPU相关专业名词介绍

CPU相关专业名词 1、CPU 中央处理器CPU&#xff08;Central Processing Unit&#xff09;是计算机的运算和控制核心&#xff0c;可以理解为PC及服务器的大脑CPU与内部存储器和输入/输出设备合称为电子计算机三大核心部件CPU的本质是一块超大规模的集成电路&#xff0c;主要功…

2024.1.21周报

目录 摘要 ABSTRACT 一、文献阅读 一、题目 二、摘要 三、Introduction 四、模型 一、连续时间模型 二、离散时间模型 五、结论 二、实验代码 Navier–Stokes equation 总结 摘要 本周我阅读了一篇题目为Physics-informed neural networks: A deep learning fra…

IDEA2023.2 将普通项目转Maven项目

1、选中一个普通工程&#xff1a; 2、快捷键&#xff1a;ctrlshift a&#xff0c;搜索&#xff1a;Add Framework Support… 3、勾选maven&#xff0c;点击ok。

信息安全概述

信息安全&#xff1a;防止任何对数据进行未授权访问的措施&#xff0c;或者防止造成信息有意无意泄漏、破坏、丢失等问题的发生&#xff0c;让数据处于远离危险、免于威胁的状态或特性。 网络安全&#xff1a;计算机网络环境下的信息安全。 信息安全现状及挑战 数字化时代威…

分布式 session

分布式 session 种 session 的时候需要注意范围&#xff0c;也就是 cookie.domain。 比如两个域名&#xff1a;a.heo.com&#xff0c;b.heo.com。如果要共享 cookie&#xff0c;可以种一个更高层的公共域名&#xff0c;比如 heo.com。 当服务器 A &#xff08;localhost:808…

构建高可用消息队列系统 01

构建高可用消息队列系统 01 引言1. RabbitMQ简介介绍1.1 什么是RabbitMQ1.2 RabbitMQ的核心特性1.3 RabbitMQ与AMQP 2.安装RabbitMQ3.消息队列实践总结 引言 在当今互联网时代&#xff0c;消息队列系统扮演着至关重要的角色&#xff0c;它们被广泛应用于分布式系统、微服务架构…

黑马Java——ArrayList集合

目录 1.ArrayList 集合和数组的优势对比&#xff1a; 1.1 ArrayList类概述 1.2 ArrayList类常用方法 1.2.1 构造方法 1.2.2 成员方法 2.练习 2.1添加字符串和数组并遍历 2.1.1添加字符串 2.1.2添加数字 2.2添加学生对象并遍历 2.2.1直接给字符串赋值 2.2.2对象的数…

【Unity学习笔记】Unity TestRunner使用

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/135733479 作者&#xff1a;CSDN|Ringleader| 参考&#xff1a; Input testingGetting started with Unity Test FrameworkHowToRunUnityUnitTest如果对Unity的newInputSystem感…

STM32 Hal库FreeRtos查看所有任务的内存栈信息使用情况剩余空间 仅需一个函数搞定 超简单

STM32F103 基于Hal库跑FreeRtos&#xff0c;查看所有任务的栈的信息&#xff0c;包括任务名&#xff0c;任务状态&#xff0c;任务优先级&#xff0c;空闲栈&#xff0c;任务号。 运行结果如下 只需要使用一个函数vTaskList &#xff1a;获得任务的统计信息&#xff0c;形式为…

2024年,给程序员的六点建议

作为程序员&#xff0c;持续进步和发展是至关重要的。除了技术能力的提升&#xff0c;还有一些关键的行为和思维方式可以帮助工程师在职业生涯中取得更大的成功。本文将提供六个重要的建议&#xff0c;这些建议将帮助程序员在职业生涯中迈出成功的步伐。 走出舒适区 走出舒适区…

HarmonyOS【应用服务开发】在模块中添加Ability

Ability是应用/服务所具备的能力的抽象&#xff0c;一个Module可以包含一个或多个Ability。应用/服务先后提供了两种应用模型&#xff1a; FA&#xff08;Feature Ability&#xff09;模型&#xff1a; API 7开始支持的模型&#xff0c;已经不再主推。Stage模型&#xff1a;AP…

CMU15-445-Spring-2023-分布式DBMS初探(lec21-24)

Lecture #21_ Introduction to Distributed Databases Distributed DBMSs 分布式 DBMS 将单个逻辑数据库划分为多个物理资源。应用程序&#xff08;通常&#xff09;并不知道数据被分割在不同的硬件上。系统依靠单节点 DBMS 的技术和算法来支持分布式环境中的事务处理和查询执…

企业SRC挖掘个人看法

昨晚上课网安的老师带我们现场挖掘漏洞&#xff0c;可谓是一场视觉盛宴&#xff01;&#xff01;&#xff01; <---老师 我--> 真的没有对比就没有伤害&#x1f62d;&#x1f62d;&#x1f62d;&#xff0c;其间他也分享了一些漏洞挖掘的思路&#xff0c;让我来回顾…

Mysql-全局锁、表锁、行锁

本文已收录于专栏 《数据库》 目录 全局锁概述说明开启方式应用场景 表锁概念说明实现方式意向锁 开启方式 行锁概念说明实现方式记录锁&#xff1a;间隙锁临键锁 总结提升 全局锁 概述说明 全局锁是是对整个数据库实例加锁&#xff0c;加锁后整个实例就处于只读状态&#xff…

智慧文旅运营综合平台:重塑文化旅游产业的新引擎

目录 一、建设意义 二、包含内容 三、功能架构 四、典型案例 五、智慧文旅全套解决方案 - 210份下载 在数字化浪潮席卷全球的今天&#xff0c;智慧文旅运营综合平台作为文化旅游产业与信息技术深度融合的产物&#xff0c;正逐渐显现出其强大的生命力和广阔的发展前景。 该…
最新文章