C++string类详解

news2024/11/15 21:52:02

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。


目录

✨标准库中的string类

  🎉string类

  🎉string类常用接口

        🌭string类对象常见构造

        🌭string类对象的容器操作

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

        🌭string类对象的修改操作

        🌭string类非成员函数

✨深拷贝与浅拷贝

  🎉浅拷贝 

  🎉深拷贝

✨string模拟实现

  🎉成员变量

  🎉构造函数&析构函数

  🎉拷贝构造函数&赋值运算符重载

  🎉对象的容器操作

        🌭size,capacity,empty,clear

        🌭reserve,resize

  🎉对象的修改操作

        🌭insert

        🌭erase

        🌭push_back,append

        🌭operator+=

        🌭find

        🌭substr

        🌭c_str

  🎉访问及遍历操作

        🌭迭代器

        🌭operator[ ]

  🎉比较运算符重载

  🎉operator<<

  🎉operator>>


✨标准库中的string类

  🎉string类

string类文档介绍

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

  🎉string类常用接口

        🌭string类对象常见构造

函数名称功能说明
string()构造空的string类对象,即空字符串
string(const char* s)用C-string来构造string类对象
string(size_t n,char c)string类对象中包含n个字符c
string(const string&s)拷贝构造函数

        🌭string类对象的容器操作

函数名称功能说明
size返回字符串有效字符长度
length返回字符串有效字符长度
capacity返回空间总大小
empty检测字符串释放为空串,空返回true,否则返回false
clear清空字符串
reserve为字符串预留空间
resize将有效字符的个数改为n个,多出的空间用字符c填充
size_t size() const;

size_t length() const;

size_t capacity() const;

 这里容量为15是因为我们底层中设计的string类是提前有预留空间的。当字符串小于规定的长度,使用内部固定的字符数组来存放;当字符串大于等于规定长度,则从对上开辟空间。

bool empty() const;

void clear();

 ​​

void reserve (size_t n = 0);

void resize (size_t n);
void resize (size_t n, char c);

  •  当原来空间大于reserve的参数,保留原来空间大小。
  • 当resize没有显示传字符c时,默认为/0。

总结:

  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不会改变容量大小。

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

函数名称功能说明
operator[ ]返回pos位置的字符,const string类对象调用
begin+endbegin获取一个字符的迭代器+end获取最后一个字符下一个位置的迭代器
rbegin+rendrbegin获取最后一个字符的迭代器+rend获取第一个字符前一个位置的迭代器
范围forC++11支持更简洁的范围for的新遍历方式

void Test_string5()
{
	string s("hello world");
	// 3种遍历方式:
	// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
	// 另外以下三种方式对于string而言,第一种使用最多
	// 1. for+operator[]
	for (size_t i = 0; i < s.size(); ++i)
	{
		cout << s[i];
	}
	cout << endl;

	// 2.迭代器
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it;
		++it;
	}
	cout << endl;

	string::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		cout << *rit ;
		++rit;
	}
	cout << endl;
	// 3.范围for
	for (auto ch : s)
	{
		cout << ch;
	}
	cout << endl;

}

        🌭string类对象的修改操作

函数名称功能说明
push_back在字符串后尾插字符c
append在字符串后追加一个字符串
operator+=在字符串后追加字符串str
c_str返回c格式字符串
find从字符串pos位置开始往后找字符c,返回该字符的位置
rfind从字符串pos位置开始往前找字符c,返回该字符的位置
substr在str中从pos位置开始,截取n个字符,然后将其返回
void push_back (char c);

string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
//一般情况下用+=,不需要用append

const char* c_str() const;

 

 

 

npos 是string里面的一个静态成员变量

static const size_t npos = -1;
void Test_string7()
{
	string filename("test.cpp.tar.zip");
	// 后缀
	size_t pos = filename.rfind('.');
	if (pos != string::npos)
	{
		//string suff = filename.substr(pos, filename.size() - pos);
		string suff = filename.substr(pos);
		cout << suff << endl;
	}
	pos = filename.find('.');
	if (pos != string::npos)
	{
		string suff = filename.substr(pos, filename.size() - pos);
		cout << suff << endl;
	}
}

        🌭string类非成员函数

函数功能说明
operator+返回一个连接后的字符串
operator>>输入运算符重载
operator<<输出运算符重载
getline获取一行字符串
relational operators比较运算符重载
istream& getline (istream& is, string& str);

标准函数库cin是提取流到分隔符或者\0的位置结束, getline是提取流直到\0(分隔符也提取) 

✨深拷贝与浅拷贝

  🎉浅拷贝 

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来 。如果 对象中管理资源 ,最后就会 导致多个对象共 享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为 还有效,所以当继续对资源进项操作时,就会发生发生了访问违规

s1的_str和s2的_str指向同一块空间,如果说s2是s1的拷贝,在函数结束时,先将s2销毁,s2将_str指向的空间释放掉,再将s1销毁,s1中_str此时已经变成野指针了,销毁s1时出错。

  🎉深拷贝

深拷贝:给每个对象独立分配资源,保证多个对象之间不会因共享资源而造成多次释放造成程序崩溃问题。

通常涉及到内存管理的拷贝构造函数、赋值运算符重载、析构函数必须显示给出。一般情况都是按深拷贝方式提供。

✨string模拟实现

  🎉成员变量

私有成员变量:

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

公有成员变量:

	public:
		//C++特例,const static特性,直接定义初始化
		const static size_t npos = -1;

  🎉构造函数&析构函数

构造函数:

		//构造函数,非自定义类型,不推崇初始化列表,全省默认传"\0"
		string(const char* str = "")
		{
			_size = strlen(str);
			_capacity = _size;
			//多开一个空间放\0
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}

析构函数:

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

  🎉拷贝构造函数&赋值运算符重载

传统写法:

拷贝构造函数:

		string(const string& s)
			:_capacity(s._capacity)
			, _size(s._size)
			, _str(new char[_capacity + 1])

		{
			strcpy(_str, s._str);
		}

赋值运算符重载:

		string& operator=(const string& s)
		{
			if (this != &s)
			{
				char* tmp = new char[s._capacity + 1];
				strcpy(tmp, s._str);
				delete[] _str;

				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

现代写法:找打工人

拷贝构造函数:

        void swap(string& tmp)
		{
            //调用标准库的swap
			::swap(_str, tmp._str);
			::swap(_capacity, tmp._capacity);
			::swap(_size, tmp._size);
		}

		string(const string& s)
			:_str(nullptr)
			, _capacity(0)
			, _size(0)
		{
            //构造tmp工具人,直接swap交换
			string tmp(s._str);
			swap(tmp);
		}

赋值运算符重载:

		string& operator=( string s)
		{
            //直接交换,原来this指向的空间还随出栈帧销毁s指向空间(原来this的空间)
			swap(s);
			return *this;
		}

  🎉对象的容器操作

        🌭size,capacity,empty,clear

		size_t size()const
		{
			return _size;
		}
		size_t capacity()const
		{
			return _capacity;
		}

		bool empty()const
		{
			return _size == 0;
		}
		void clear()
		{
			_str[0] = '\0';
			_size = 0;
		}

        🌭reserve,resize

        //开空间
		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;
				}
				_str[n] = '\0';
				_size = n;

			}
			else
			{
				//删除数据
				_str[n] = '\0';
				_size = n;
			}
		}

  🎉对象的修改操作

        🌭insert

        指定位置插入

        string& insert(size_t pos,char ch)
		{
			assert(pos <= _size);
			//满了扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			//末尾后一个
			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)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			//满了扩容
			if (_size +len> _capacity)
			{
				reserve(_size+len);
			}
			//末尾后len个位置
			size_t end = _size + len;
			while (end >= pos+len)
			{
				//数据后移
				_str[end] = _str[end - len];
				--end;
			}
			strncpy(_str+pos,str,len);
			_size += len;
			return *this;
		}

        🌭erase

        void 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;
			}
		}

        🌭push_back,append

        void push_back(char ch)
		{
			//复用
			insert(_size, ch);
		}

		void append(const char* str)
		{

			//复用
			insert(_size, str);
		}

        🌭operator+=

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

        🌭find

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

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

		}

        🌭substr

        //从pos开始截取一段指定长度字符串
		string substr(size_t pos, size_t len = npos) const
		{
			assert(pos < _size);
			//截取字符串的真实长度
			size_t realLen = len;
			if (len == npos || pos + len > npos)
			{
				realLen = _size - pos;
			}


			string sub;
			for (size_t i = 0; i < realLen; i++)
			{
				sub += _str[pos + i];
			}
			return sub;
		}

        🌭c_str

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

  🎉访问及遍历操作

        🌭迭代器

        typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}

        🌭operator[ ]

		const char& operator[](size_t pos)const
		{
			assert(pos < _size);
			return _str[pos];
		}
		char& operator[](size_t pos)
		{
			assert(pos < _size);
			return _str[pos];
		}

  🎉比较运算符重载

只需要写>,==或者<,==,其他复用即可。

		bool operator>(const string& s) const
		{
			return strcmp(_str, s._str) > 0;
		}

		bool operator==(const string& s) const
		{
			return strcmp(_str, s._str) == 0;
		}

		bool operator>=(const string& s) const
		{
			return (*this > s) || (*this == s);
		}

		bool operator<=(const string& s) const
		{
			return !(*this > s);
		}

		bool operator<(const string& s) const
		{
			return !(*this >= s);
		}

		bool operator!=(const string& s) const
		{
			return !(*this == s);
		}

  🎉operator<<

    //流提取
	ostream& operator<<(ostream& out, const string& s) 
	{
		for (int i = 0; i < s.size(); i++)
		{
			out << s[i];
		}
		return out;
	}

  🎉operator>>

	//流插入
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		ch = in.get();
		//类似缓冲区思路
		const size_t N = 32;
		char buff[N];
		size_t i= 0;
		while (ch != ' ' && ch != '\0')
		{
			buff[i++] = ch;
			if (i == N - 1)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		buff[i] = '\0';
		s += buff;
		return in;
	}

开篇说的预留空间,字符串+=字符数组


 最后再说一个知识点:写时拷贝

C++ STL string的Copy-On-Write技术 | 酷 壳 - CoolShell

写时拷贝是在浅拷贝的基础上增加了引用计数的方式实现。

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

 

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

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

相关文章

【0基础学爬虫】爬虫基础之自动化工具 Selenium 的使用

大数据时代&#xff0c;各行各业对数据采集的需求日益增多&#xff0c;网络爬虫的运用也更为广泛&#xff0c;越来越多的人开始学习网络爬虫这项技术&#xff0c;K哥爬虫此前已经推出不少爬虫进阶、逆向相关文章&#xff0c;为实现从易到难全方位覆盖&#xff0c;特设【0基础学…

巨型AI模型时代已结束,我们没搞GPT-5,搞的是GPT-4.99999

文章目录 1、ChatGPT 研发热潮2、GPT5 被叫停“AI危险竞赛”3、 叫停是无法被阻止的4 、 不急于训练GPT-5 1、ChatGPT 研发热潮 自ChatGPT重新吹响人工智能革命的号角后&#xff0c;“百模大战”也已然在太平洋两岸同时拉开了帷幕。 近几个月来&#xff0c;OpenAI ChatGPT 的…

记frp内网穿透配置

这两天由于想给客户看一下我们的系统&#xff0c;于是想到用内网穿透&#xff0c;但是怎么办呢&#xff0c;没有用过呀&#xff0c;于是各处找资料&#xff0c;但是搞完以后已经不记得参考了那些文档了&#xff0c;对不起各位大神&#xff0c;就只能写出过程和要被自己蠢死的错…

一文了解,AI圈大火的虚拟数字人到底是什么?

近年来&#xff0c;人工智能技术的发展和应用已经成为科技领域的热门话题。AI不仅可以帮助人们解决各种问题&#xff0c;还可以提高生产效率、改善生活质量等方面做出贡献。而虚拟数字人作为AI技术的一种应用&#xff0c;也在不断地发展和应用&#xff0c;为人们带来更多的便利…

maven安装教程 linux

文章目录 1.maven下载1.1 移动压缩包位置1.2 解压1.3 改名 2. maven 配置修改2.1 创建仓库2.2 编辑settings.xml文件2.3 添加环境变量 1.maven下载 由于maven 暂不支持直接用yum安装遂采用此方法安装 点击链接下载apache-maven-3.6.3-bin.tar.gz wget https://archive.apach…

Javaee Spring的AOP简介

一.Spring的AOP简介 1.1 什么是AOP AOP 为 Aspect Oriented Programming 的缩写&#xff0c;意思为面向切面编程&#xff0c;是通过预编译方式和运行期动态代 理实现程序功能的统一维护的一种技术。AOP 是 OOP 的延续&#xff0c;是软件开发中的一个热点&#xff0c;也是…

人工智能论文的风格特点

搞清楚AI领域论文的风格特点是写出一篇高质量AI论文的前提&#xff0c;AI领域的论文有如下显著特点。 1. 论文的架构非常清晰且富有逻辑。一篇高质量的AI论文&#xff0c;读者通过大致扫一眼论文的各级标题就能够对论文的写作思路形成清晰的认识&#xff0c;明白论文各部分之间…

防火墙日志取证及分析

防火墙日志取证 网络安全解决方案的主要目的是保护网络免受攻击。它应监视安全事件并实时提醒&#xff0c;以帮助管理员尽快采取补救措施。此外&#xff0c;您需要深入的信息来分析任何漏洞的根本原因、攻击事件重建和用户活动;这就是取证日志分析的用武之地。 取证日志分析软…

广州蓝景分享—遇到网页慢,我们该怎么办?

前言 移动互联网时代&#xff0c;用户对于网页的打开速度要求越来越高。首屏作为直面用户的第一屏&#xff0c;其重要性不言而喻。优化用户体验更是我们前端开发非常需要 focus 的东西之一。 从用户的角度而言&#xff0c;当打开一个网页&#xff0c;往往关心的是从输入完网页…

计算机:理解操作系统:内存篇(中)

内存 1.堆和栈的本质是什么2. java、Python等内存模型3. java内存模型3.1 java中堆和栈是如何实现的 4. Python内存模型 什么是内存 C/C内存模型 堆区与栈区的本质 Java、Python等内存模型 Java内存模型 Jave中的堆区与栈区是如何实现的 Python内存模型 指针与引用 进程的内存…

企业网站架构部署与优化 LNMP

【安装 Nginx 服务】 systemctl stop firewalld systemctl disable firewalld setenforce 0 1、安装依赖包 yum -y install pcre-devel zlib-devel gcc gcc-c make 2、创建运行用户 useradd -M -s /sbin/nologin nginx 3、编译安装 cd /opt tar zxvf nginx-1.12.0.tar.gz -C …

Apifox软件的基础使用方式

Apifox软件的基础使用方式 简单方便的用途 该工具是接口在线调试工具&#xff0c;这里我给到连接供大家去官网下载&#xff0c;我个人觉得是比较于postman工具好用&#xff0c;提供的语言操作是中文版本的便于操作 下载和安装 https://apifox.com/?utm_sourcebaidu&ut…

【超详细】【YOLOV8使用说明】一套框架解决CV的5大任务:目标检测、分割、姿势估计、跟踪和分类任务【含源码】

目录 1.简介2.环境安装2.1安装torch相关库2.2 获取yolov8最新版本&#xff0c;并安装依赖 3. 如何使用模型用于各种CV任务3.1 目标检测任务实现检测图片代码检测视频代码 3.2 分割任务实现分割图片代码分割视频代码 3.3 追踪任务3.4 姿态检测任务姿态检测&#xff08;图片&…

27.Linux网络编程 掌握三次握手建立连接过程掌握四次握手关闭连接的过程掌握滑动窗口的概念掌握错误处理函数封装实现多进程并发服务器实现多线程并发服务器

基本概念叫协议 什么叫协议? 协议是一个大家共同遵守的一个规则&#xff0c; 那么在这个网络通信当中&#xff0c;其实就是双方通信和解释数据的一个规则&#xff0c;这个概念 你也不用记&#xff0c;你只要心里明白就可以了&#xff0c; 分层模型&#xff0c; 物数网传会表应…

Tensorflow GPU 版本安装教程

非常详细的 Tensorflow GPU 版本安装教程 一、安装Anaconda二、TensorFlow GPU 一、安装Anaconda 这一步比较简单&#xff0c;也没有太多的需要注意的&#xff0c;去官网下载即可&#xff1a; 官网地址如下&#xff1a; https://www.anaconda.com/blog/individual-edition-2…

百家云在人工智能领域再有新动作,发布应用于多个行业的AIGC解决方案

4月17日消息&#xff0c;音视频SaaS上市公司百家云&#xff08;股票代码&#xff1a;RTC&#xff09;今日宣布&#xff0c;公司将正式推出应用于多个垂直行业及场景的人工智能生成内容及视频解决方案。 百家云总裁马义表示&#xff0c;此次发布的解决方案&#xff0c;将在极短…

谷歌SEO优化技巧方法

谷歌SEO排名对于许多公司和个人来说都非常重要。随着谷歌成为人们搜索信息的首选&#xff0c;拥有良好的谷歌排名可以帮助我们的网站在搜索引擎中展现出更高的可见度&#xff0c;吸引更多的访问量和潜在客户。优化谷歌SEO排名需要一定的时间和专业知识&#xff0c;无法一蹴而就…

Vulnhub项目:Lin.Security(Linux提权大合集)

靶机地址&#xff1a;linsecurity 靶机描述了用户名和密码 用户名&#xff1a;bob、密码&#xff1a;secret 登录后看到了IP地址&#xff0c;或者在kali上像之前那样进行ip收集&#xff0c;端口收集 开放的端口有很多&#xff0c;从22端口开始&#xff0c;知道用户名和密码&…

供水管网爆管预测模型研究现状

配水管网是供水系统中最昂贵的组成部分。管网运行管理和调度技术难度高&#xff0c;爆管事故 容易发生&#xff0c;对人民生活、工业生产、城市交通及社会安定造成不利影响&#xff0c;造成严重的经济损 失。合理的管道的修复更新计划是满足供水目标、实现对管网的科学管理的重…

回炉重造五--软件管理

1.软件管理 1.1软件包中的文件分类 二进制文件库文件配置文件帮助文件 1.2包查询–rpm -a&#xff1a;所有包 -p:针对尚未安装的程序包文件做查询工作 -l:查看指定的程序包安装后生成的所有文件 -i:查看包的信息 #常用的查询方法 -qa/q/qi2、yum和dnf 2.1 yum/dnf工作原理…