【c++】string深度刨析以及实现

#pragma once
#include<iostream>
using namespace std;
#include<assert.h>
namespace bite
{
	class string
	{
	public:
		//迭代器  //像指针 底层不一定是指针 
		typedef char* iterator;
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}

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

		
	/*	string()
			:_str(nullptr)
			, _size(0)
			,_capacity(0)
		{
		}*/

		//string(char* str)    //常量字符串不能直接赋值
		//	:_str(new char[strlen(str) + 1])  //不是很好,strlen是在运行时,所以不适宜多调用
		//	, _size(strlen(str))
		//	,_capacity(strlen(str))
		//{
		//	strcpy(_str, str);
		//}
		// string(const char* str="\0");  可以但是实际不推荐  因为\0的后面还有\0
		//string(const char* str=nullptr)  缺省参数,strlen不能初始化为空

		const char* c_str() const
		{
			return _str;
		}
	
		//二合一
		//构造
		string(const char* str="")
			:_size(strlen(str))
		{
			_str = new char[_size+1];
			_capacity = _size;
			strcpy(_str, str);
		}
		
		//拷贝构造s2(s1)
		//传统写法
		//string(const string &s)
		//{
		//	_str = new char[s._capacity+1];
		//	strcpy(_str, s._str);
		//	_size = s._size;
		//	_capacity = s._capacity;
		//}
		//
		s1=s3
		//string& operator=(const string& s)
		//{
		//	char* tmp = new char[s._capacity + 1];
		//	strcpy(tmp, s._str);
		//	delete[] _str;
		//	_str = tmp;
		//	_size = s._size;
		//	_capacity = s._capacity;
		//	return *this;
		//}

		//现代写法   本质是一种复用  假他人之手
		string(const string& s)
			:_str(nullptr)
		{
            //调用构造
			string stmp(s._str);
            //this和tmp进行交换
			swap(stmp);
		}

         //s1==s3
		//string& operator=(const string&ss)
		//{
		//	//调用拷贝构造
		//	string tmp(ss);
		//	swap(tmp);
		//	return *this;
		//}

		string& operator=(string tmp)
		{
			swap(tmp);
			return *this;
		}

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

		//下标+【】遍历
		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 resize(size_t n, char ch = '\0')
		{
			if (n < _size)
			{
				_str[n] = '\0';
				_size = n;
			}
			else
			{
				reserve(n);
				for (size_t i = _size; i < n; ++i)
				{
					_str[i] = ch;
				}
				_str[n] = '\0';
				_size = n;
			}
		}

		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char*tmp = new char[n+1];//多开一个‘\0’
				strcpy(tmp, _str);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
		}

		void push_back(char ch)
		{
			//扩容2倍
		/*	if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			_size++;
			_str[_size] = '\0';*/

			insert(_size,ch);

			
		}

		void append(const char* str)
		{
			扩容
			//int len = strlen(str);
			//if (_size + len > _capacity)
			//{
			//	reserve(_size + len);
			//}
			//strcpy(_str + _size, str);
			//_size += len;
			insert(_size, str);
		}

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

		//pos位置插入
		void insert(size_t pos, char ch)
		{
			//等于_size就是尾插
			assert(pos <= _size);
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			size_t end = _size+1;
			//
			//这么写头插时因为end是size_t会发生整形提升
			//while (end>=pos)
			/*while (end >= pos)

			{
				_str[end + 1] = _str[end];
				--end;
			}*/

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

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

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

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

		//pos位置删除len个字符
		void erase(size_t pos,size_t len = npos)
		{
			assert(pos < _size);
			//pos+len len处于接近阈值的时候会出现溢出的风险
			//if (len == npos || pos + len >= _size)
			//解决溢出风险
			if (len == npos || _size-pos <=len)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				strcpy(_str+pos,_str+pos+len);
				_size -=len;
			}
		}

		void swap(string& s)
		{
			//就近原则,去全局找
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}

		//查找位置 ,返回下标
		size_t find(char ch,size_t pos=0) const
		{
			assert(pos < _size);
			for (int i = pos; i < _size; ++i)
			{
				if (_str[i]==ch)
				{
					return i;
				}
			}
			return npos;
		}


		size_t find(const char*sub, size_t pos = 0) const
		{
			assert(pos < _size);
			const char* p = strstr(_str+pos, sub);

			if (pos)
			{
				return p - _str;
			}
			else
			{
				return npos;
			}
		}

		string substr(size_t pos=0, size_t len = npos)
		{

			string sub;
			//if(pos==npos||len>=_size-pos)
			if (len >= _size - pos)
			{
				for (size_t i = pos; i < _size; ++i)
				{
					sub += _str[i];
				}

			}
			else
			{
				for (size_t i = pos; i < pos + len; ++i)
				{
					sub += _str[i];
				}
			}
			return sub;

		}

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

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

	public:
		//共有的静态成员变量
		static const int npos;
	};
	const int string::npos = -1;


	bool operator==(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret == 0;
	}

	bool operator>(const string& s1, const string& s2)
	{
		int ret = strcmp(s1.c_str(), s2.c_str());
		return ret > 0;
	}

	bool operator>=(const string& s1, const string& s2)
	{
		return s1 > s2 || s1 == s2;
	}

	bool operator<(const string & s1, const string & s2)
	{
		return !(s1 >= s2 );
	}


	bool operator<=(const string& s1, const string& s2)
	{
		return !(s1 > s2);
	}


	void swap(string& x, string& y)
	{
		x.swap(y);
	}

	ostream& operator<<(ostream& out, const string& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}

	
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char ch;
		//会出现死循环的情况 scanf和cin默认空格和换行做分割符无法取到
		//in >> ch;
		//get遇到一个字符娶一个字符
		ch = in.get();
		while (ch!=' '&&ch!='\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}


	void test_string1()
	{
		string s1("hello world");

		for (size_t i = 0; i < s1.size(); ++i)
		{
			cout << s1[i] << " ";
		}
		cout << endl;

		string s2("hello worrld");
		string::iterator it = s2.begin();
		while (it!=s2.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl;

		s2.push_back('s');
		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl;

		s2.append("xxxxx");
		for (auto ch : s2)
		{
			cout << ch << " ";
		}
		cout << endl; 
		
		const string s3("hello world");
		for (auto ch : s3)
		{
			cout << ch << " ";
		}
		cout << endl;

		//迭代器不一定是原生指针,typeid可以查看原生类型
		cout << typeid(std::string::iterator).name() << endl;
	}

	void test_string2()
	{
		string s1("hello world");
		s1 += 'x';
		s1 += "ssss";
		s1.insert(0,'o');
		for (auto ch : s1)
		{
			cout << ch << " ";
		}
		cout << endl;

	}

	void test_string3()
	{
		string s3("hello world");
		for (auto ch : s3)
		{
			cout << ch << " ";
		}
		cout << endl;
		s3.erase(5, 3);
		cout << s3.c_str() << endl;
	}


	void test_string4()
	{
		string s1("hello world");
		cout << s1.c_str() << endl;

		//没实现拷贝构造时
		//程序崩溃,析构的时候对同一块空间多次析构
		//发生浅拷贝,值拷贝
		string s2(s1);
		cout << s2.c_str() << endl;

		s2.insert(5, "xxxxx");
		cout << s2.c_str() << endl;
	}


	void test_string5()
	{
		string s1("hello world");
		string s2("hello world");
		cout << s1.c_str() << endl;
		cout << s2.c_str() << endl;
       //深拷贝代价:三次拷贝加一次析构 
		//swap(s1, s2);
		//s1.swap(s2);

		//全局重载
		swap(s1, s2);
		cout << s1 << endl;
		cout << s2 << endl;

		cout << (s1 == s2) << endl;


		string s4;
		string s5;
		cin >> s4 >> s5;
		cout << s4 << endl;
		cout << s5 << endl;

	}
}

     vs和g++下string结构的说明      

  注意:下述结构是在32位平台下进行验证,32位平台下指针占4个字节

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个字节。

g++下string的结构 

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

  • 空间总大小
  • 字符串有效长度
  • 引用计数 (解决浅拷贝析构多次的问题)   有引用计数以后,先--引用技术 ,如果减完以后,引用技术==0 代表当前是最后一个管理资源的对象,那就释放。

浅拷贝的修改会影响其他对象的问题 :

检查引用计数,如果等于1,说明资源是自己独占的,不用拷贝
如果大于1,先拷贝再写,这就是写时拷贝

反正都要拷贝,意义是什么?

不是每个对象都会修改,拷贝后不修改就赚   

struct _Rep_base
{
 size_type _M_length;
 size_type _M_capacity;
 _Atomic_word _M_refcount;
};

指向堆空间的指针,用来存储字符串。 

写时拷贝

写时拷贝在读取时的缺陷 

扩展阅读

面试中string的一种正确写法 

STL中的string怎么了 

 

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

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

相关文章

RERCS系统-WDA+BOPF框架实战例子 PART 2-新建Root的子节点Node Element

1、通过事务码 BOBF进入Business Object Browser&#xff08;业务对象浏览&#xff09;页面&#xff1b; 2、输入debug 进入编辑模式&#xff1b; 3、双击对应的业务对象进入Business Object Detail Browser即业务对象数据浏览器 在Node Structure的Root中新建需要的SubNode子…

OpenCV使用 Kinect 和其他兼容 OpenNI 的深度传感器(75)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:使用 OpenCV 创建视频(74) 下一篇 :OpenCV使用 Orbbec Astra 3D 相机(76) 目的&#xff1a;​ 通过 VideoCapture 类支持与 OpenNI 兼容的深度传感器&#xff08;Kinect、XtionPRO 等&#xff09;。…

力扣HOT100 - 215. 数组中第K个最大元素

解题思路&#xff1a; 快速选择&#xff0c;目标是找出数组中第 k 小&#xff08;或第 k 大&#xff09;的元素&#xff0c;而不是对整个数组进行排序。 &#xff08;需要和快排进行区分&#xff0c;快排的目的是排序&#xff09; 注意&#xff1a; i l - 1, j r 1; 为什…

leetcode刷题指南

本文我将分享给大家一套我自己使用良久并觉得非常高效的 学习论&#xff0c;它可以运用到 Leetcode 上的刷题&#xff0c;也可以 generalize 到生活中涉及到学习以及记忆的方方面面。当然&#xff0c;本文将以 Leetcode 刷题为 case study 去进行讲解。 更具体一点, 我会教大家…

鸿蒙OpenHarmony开发板解析:【系统能力配置规则】

如何按需配置部件的系统能力 SysCap&#xff08;SystemCapability&#xff0c;系统能力&#xff09;是部件向开发者提供的接口的集合。 开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 部件配置系统…

Received Signals.SIGHUP death signal, shutting down workers

单机多卡训练大模型的时候&#xff0c;突然报错&#xff1a; 3%|▎ | 146/4992 [2:08:21<72:57:12, 54.20s/it][2024-05-10 13:27:11,479] torch.distributed.elastic.agent.server.api: [WARNING] Received Signals.SIGHUP death signal, shutting down workers [2…

Java转Kotlin调用JNI方法异常

一、背景 Java调用JNI方法时没有任何问题&#xff0c;但是使用Java转Kotlin以后出现了崩溃异常&#xff1a;A java_vm_ext.cc:597] JNI DETECTED ERROR IN APPLICATION: jclass has wrong type: 校验参数后没有任何变化&#xff0c;经过分析验证找到解决方案 二、原因…

Java入门基础学习笔记16——运算符

package cn.ensource.operator;public class OperatorDemo1 {public static void main(String[] args) {// 目标&#xff1a;掌握基本的算术运算符的使用int a 10;int b 2;System.out.println(a b);System.out.println(a - b);System.out.println(a * b); // 20System.out.…

4步快速配置Java、MySQL、Maven环境(windows)

每次入职一家新公司或者用一台其他的临时电脑或者新电脑时都要重新配置Java开发环境&#xff0c;很麻烦&#xff0c;因此我在这里记录一下快速配置环境的方式&#xff0c;四步搞定&#xff01;此处以win为操作系统进行讲解。 第一步&#xff1a;下载链接 下载链接&#xff1a…

【半夜学习MySQL】数据库中的数据类型(含数值类型、文本二进制类型、时间类型、String类型详谈)

&#x1f3e0;关于专栏&#xff1a;半夜学习MySQL专栏用于记录MySQL数据相关内容。 &#x1f3af;每天努力一点点&#xff0c;技术变化看得见 文章目录 数据类型分类数值类型bit类型tinyint类型int类型float类型decimal类型 文本、二进制类型char类型varchar类型 时间类型Strin…

51单片机入门:串口通信

串行通信的初步认识 通信方式分类 1、按照数据传送方式&#xff1a; 并行通信&#xff1a;通信时数据的各个位同时传送&#xff0c;可以实现字节为单位的通信。 但是通信线多&#xff0c;占用资源多&#xff0c;成本高。 串行通信&#xff1a;一次只能发送一位&#xff0c…

机器学习-Numpy

机器学习-Numpy 如果一个人拒绝提高自己的思想觉悟&#xff0c;那么他只能处在弱小、可怜、凄惨的境地。 目录 机器学习-Numpy 1.Numpy&#xff1a;生成矩阵 做矩阵运算 1&#xff09;创建矩阵 ①使用列表创建 ②使用元组创建 2&#xff09;矩阵取值 3&#xff09;numpy…

【栈】Leetcode 字符串解码

题目讲解 394. 字符串解码 算法讲解 这道题有四种情况&#xff1a;1.遍历的时候遇到数字&#xff0c;我们计算并保存数字&#xff0c;将它加入到数字栈中&#xff1b;2.遍历的时候遇到[&#xff0c;我们就把字符保存&#xff0c;加入到字符栈中&#xff1b;3.当遇到]&#x…

全栈开发之路——前端篇(9)插槽、常用api和全局api

全栈开发一条龙——前端篇 第一篇&#xff1a;框架确定、ide设置与项目创建 第二篇&#xff1a;介绍项目文件意义、组件结构与导入以及setup的引入。 第三篇&#xff1a;setup语法&#xff0c;设置响应式数据。 第四篇&#xff1a;数据绑定、计算属性和watch监视 第五篇 : 组件…

【2024亚马逊云科技峰会】Amazon Bedrock + Llama3 生成式AI实践

在 4 月 18 日&#xff0c;Meta在官网上公布了旗下最新大模型Llama 3。目前&#xff0c;Llama 3已经开放了80亿&#xff08;8B&#xff09;和700亿&#xff08;70B&#xff09;两个小参数版本&#xff0c;上下文窗口为8k&#xff0c;据称&#xff0c;通过使用更高质量的训练数据…

HTML实现3D相册

目录 写在前面 HTML简介 完整代码 代码分析 注意事项 系列推荐 写在最后 写在前面 本期小编给大家推荐一个炫酷的3D相册&#xff0c;可以更换照片哦&#xff0c;一起来看看吧~ HTML简介 HTML&#xff0c;即HyperText Markup Language&#xff0c;是一种广泛应用的超文…

扩展van Emde Boas树以支持卫星数据:设计与实现

扩展van Emde Boas树以支持卫星数据&#xff1a;设计与实现 1. 引言2. vEB树的基本概念3. 支持卫星数据的vEB树设计3.1 数据结构的扩展3.2 操作的修改3.3 卫星数据的存储和检索 4. 详细设计和实现4.1 定义卫星数据结构体4.2 修改vEB树节点结构4.3 插入操作的伪代码4.4 C语言实现…

GPIO输出速度(ARM-GD32)

单片机输出速度对GPIO硬件的影响 如果T为100ns 那么2/3*100ns 67ns 那么tr tf 38 ns &#xff08;也就是不能超过32ns&#xff09; tr 和tf和什么东西有关如何去控制 CL 是一个电容&#xff0c;电容会改变和影响电压变化的速率&#xff0c;输出高低电平也就是对电容进行充电…

【DDR 终端稳压器】Sink and Source DDR Termination Regulator [A]

Sink Source 这两个词被翻译的有点混乱了&#xff0c;有点“输入”“输出”的意思&#xff0c;但是还是不准确&#xff1b; 1 Sink 去到英英词典看看&#xff0c;母语是怎么介绍的吧。 to go down below the surface or towards the bottom of a liquid or soft substances…

uniapp 版本检查更新

总体来说uniapp的跨平台还是很不错的&#xff0c;虽然里面各种坑要去踩&#xff0c;但是踩坑也是开发人员的必修课和成长路。 这不&#xff0c;今天就来研究了一下版本检查更新就踩到坑了。。。先来看看检查更新及下载、安装的实现。 先来看看页面&#xff1a; 从左到右依次为…