Lesson08---string(4)类

news2024/9/27 8:33:35

Lesson08—string类(4)

c++第八章string类的实现


文章目录

  • Lesson08---string类(4)
  • 前言
  • 一、计算机是怎么储存文字的
    • 1. 在此之前先思考一个问题
    • 2.编码表
      • 2.1 ascll码
      • 2.2unicode码
      • 2.3UTF码
      • 2.4gbk码
  • 二、实现一个简单的string
    • 1.构造函数和析构函数
    • 2.范围for和迭代器
    • 3.常见的成员函数
      • 3.1 size
      • 3.2 reserve
      • 3.3 push_back
      • 3.4 append
      • 3.5 insert(insert(size_t pos, char ch))
      • 3.6 void insert(size_t pos, const char* str);
      • 3.7 void erase(size_t pos = 0, size_t len = npos);
      • 3.8 size_t find(char ch, size_t pos=0);
      • 3.9 size_t find(const char* sub, size_t pos = 0);
      • 3.10 拷贝构造函数 My_String(const My_String& str);
      • 3.11赋值函数 My_String& operator=(My_String s);
      • 3.12 My_String substr(size_t pos = 0, size_t len = npos);
    • 4.运算符重载
    • 5.比较运算符 重载
    • 6.流插入和提取


前言

这篇文章写了计算机是怎么存储文字的,以及string类的底层是怎么实现的


一、计算机是怎么储存文字的

1. 在此之前先思考一个问题

#define _CRT_SECURE_NO_WARNINGS 
#include<bits/stdc++.h>
using namespace std;
int main()
{
    char buff1[] = "abcd";
    char buff2[] = "你好";
    cout << sizeof(buff1) << endl;
    cout << sizeof(buff2) << endl;

    return 0;
}

上面的代码输出是多少?


在这里插入图片描述
这就很奇怪为什么数量不一样需要的字节却是一样的?

为什么第一个数组明明只有4的字母要的内存却是5的?

通过调试来看看
在这里插入图片描述
这里可以看到第一个数组里面存的其实是ascll码,内存里面那个61.62其实就是97的16进制,比实际的数量多1是因为\0,中文是俩个字节

2.编码表

2.1 ascll码

在计算机里面只有010101,它也只认识0101010其他的语言它必须翻译成01010计算机它才能认识,最开始发明计算机的大佬用的是英语,大佬们就对英语进行了编码,对26个字母进行了映射,以及一些标点符号

在这里插入图片描述
在这里插入图片描述
计算机内存里面只会存ascii码,显示的时候就去找这个表然后显示出来


在这里插入图片描述
早年间计算机只有在美国那边使用,所以最开始就只弄了英文和一些标点符号,但随着计算机的普及一种语言早以满足不了人们

在这里插入图片描述
ascll码的取值范围是0-127,那么上面的负数到底是啥?


2.2unicode码

英文就26个大小一共也就26*2加上一些符号上面的加起来差不多也就100多个,所有的单词都是这些组成,那么中文呢?

汉字十几万个难道也去做一个十几万个的映射表?


unicode也就是统一码,它诞生就是为了编码全世界的文字
在这里插入图片描述
在这里插入图片描述
大部分的文字用俩个字节就足以存下,但这里又会出现一个新的问题,如果中英混合怎么办?


2.3UTF码

如果想实现混合编码那么这几套就必须要兼容,utf码提供了三种编码
在这里插入图片描述
用的最多的就是utf8

2.4gbk码

中华的文化博大精深就比如中文不仅有简体字还有繁体字utf就没有gbk处理中文处理的好,gbk就是专门处理中文的一种编码在这里插入图片描述

二、实现一个简单的string

1.构造函数和析构函数

我这里采用多文件的形式

//string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class My_String
{
	friend ostream& operator<<(ostream& out, My_String& string);
public:
	My_String(const char* str="");
	~My_String();
	const char* c_str()const;
private:
	char* _str;
	size_t _size;
	size_t _capacity;

};








在这里插入图片描述


#include"string.h"
My_String::My_String(const char* str)
	:_size(strlen(str))

{

	_str = new char[_size + 1];
	_capacity = _size;
	strcpy(_str, str);
}

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

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

ostream& operator<<(ostream& out,  My_String& string)
{
	cout << string._str;
	return out;
}

在这里插入图片描述

2.范围for和迭代器

//string.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
class My_String
{
	friend ostream& operator<<(ostream& out, My_String& string);
public:
	typedef char* iterator;


	iterator begin(); 

	iterator end();
	My_String(const char* str="");
	~My_String();
	const char* c_str()const;
	size_t size()const;
	char& operator[](size_t pos);

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

};


在这里插入图片描述

//string.cpp
#include"string.h"




My_String::iterator My_String::begin()
{
	return _str;
}

My_String::iterator My_String::end()
{
	return _str+_size;
}


My_String::My_String(const char* str)
	:_size(strlen(str))

{

	_str = new char[_size + 1];
	_capacity = _size;
	strcpy(_str, str);
}

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

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

ostream& operator<<(ostream& out,  My_String& string)
{
	cout << string._str;
	return out;
}
size_t My_String::size()const
{
	return _size;
}
char& My_String::operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}

在这里插入图片描述
这里需要注意命名最好养成习惯,要不然就会出现各种各样的问题
在这里插入图片描述
这样以后就可以用迭代器和范围for来遍历这个数组了,范围for的底层就是迭代器

3.常见的成员函数

3.1 size

这个比较简单就不过多赘述
在这里插入图片描述

3.2 reserve

这个函数有保留的意思这里我拿来扩容,细节可以参考https://blog.csdn.net/m0_67371175/article/details/141872058?spm=1001.2014.3001.5502的第七条

void My_String::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[]_str;
		_capacity = n;
		_str = tmp;
	}
}

在这里插入图片描述

3.3 push_back

这个函数在string容器里面比较复杂我这里就简单实现一下

void My_String::push_back(char ch)
{
	size_t newcapacity;
	if (_size == _capacity)
	{
		if (_capacity == 0)
		{
			newcapacity = 4;
		}
		else
		{
			newcapacity = _capacity * 2;
		}
		My_String::reserve(newcapacity);
	}

	_str[_size] = ch;
	_str[_size + 1] = '\0';
	++_size;
}

在这里插入图片描述

3.4 append

这里append和push_back的区别就是一个是字符一个是字符串这里我append是尾插字符串

void My_String::append(const char* str)
{
	size_t len = strlen(str);
	if (_size+len>_capacity)
	{
		reserve(_size + len);
	}
	strcpy(_str+_size, str);
	_size += len;
}

在这里插入图片描述

3.5 insert(insert(size_t pos, char ch))

下面代码运行结果是死循环,你找的到问题出在哪里吗
错误1:

void My_String::insert(size_t pos, char ch)
{
	if (_size == _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newcapacity);
	}

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

	}

	_str[pos] = ch;
	_size++;
	

}

在这里插入图片描述

在这里插入图片描述
因为这里的end是size_t类型没有符号所以直接就变成了最大的正数了
在这里插入图片描述
错误2:
在这里插入图片描述
把end换成int类型还是死循环你知道为什么吗?

在这里插入图片描述

int类型和size_t类型比较发生隐式类型转换,int类型转换成了size_t了
在这里插入图片描述
解决方法就是把pos强制转换成int
在这里插入图片描述
这样就可以解决这个问题
或者也可以这样
在这里插入图片描述

3.6 void insert(size_t pos, const char* str);

void My_String::insert(size_t pos, const char* str)
{
	size_t len = strlen(str);

	if (_size + len > _capacity)
	{
		reserve(_size + len);
	}

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

	_size += len;
	memcpy(_str + pos, str, len);
}
size_t len = strlen(str);

if (_size + len > _capacity)
{
	reserve(_size + len);
}

也可以这么写

size_t len = strlen(str);

if (_size + len > _capacity)
{
	reserve(_size + len);
}
size_t end = _size + len;

while (end>= pos+len)
{
	_str[end]=_str[end - len];
	end--;
		
}
memcpy(_str + pos, str, len);
_size += len;

insert函数写好了以后尾插就可以直接调用这个函数了

3.7 void erase(size_t pos = 0, size_t len = npos);

//下面代码还有一个错误可以尝试找一下
void My_String::erase(size_t pos, size_t len)
{
	assert(pos < _size );
	if (pos+len>=_size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

在这里插入图片描述
但是这样写就没错

void My_String::erase(size_t pos, size_t len)
{
	assert(pos < _size );
	if (len>=_size - pos)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
		strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
}

在这里插入图片描述
我这里len的缺省值是npos,npos是size_t n=-1,然后它是无符号整型纯的补码,也就是111111…也就是size_t的最大值,如果在+一个数就会溢出

3.8 size_t find(char ch, size_t pos=0);

size_t My_String::find(char ch, size_t pos)
{
	for (size_t i = pos	; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}

	return npos;
}

3.9 size_t find(const char* sub, size_t pos = 0);

size_t My_String::find(const char* sub, size_t pos)
{
	char* p = strstr(_str + pos, sub);
	return p - _str;
}

3.10 拷贝构造函数 My_String(const My_String& str);

My_String::My_String(const My_String& str)
{
	_str = new char[str._capacity + 1];
	_size = str._size;
	_capacity = str._capacity;
	strcpy(_str, str._str);
}

3.11赋值函数 My_String& operator=(My_String s);

My_String& My_String::operator=(My_String s)
{
	if (this != &s) {
		char* temp = new char[s._capacity + 1];
		strcpy(temp, s._str);
		delete[] _str;
		_str = temp;
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

3.12 My_String substr(size_t pos = 0, size_t len = npos);

My_String My_String::substr(size_t pos, size_t len)
{
	if (len > _size - pos)
	{
		My_String sub(_str + pos);
		return sub;
	}
	else
	{
		My_String sub;
		sub.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			sub += pos + i;
		}
		return sub;
	}
}

4.运算符重载

这 里只重载+=比较有意义就先重载这一个

My_String& My_String::operator+=(char ch)
{
	push_back(ch);
	return *this;

}

My_String& My_String::operator+=(const char* str)
{
	append(str);
	return *this;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
加上const以后就不再报错


5.比较运算符 重载

bool My_String::operator<(const My_String& s) const
{
	return strcmp(_str,s._str) < 0;
}

bool My_String::operator>(const My_String& s) const
{
	return !(*this <= s._str);
}

bool My_String::operator<=(const My_String& s) const
{
	return *this < s || *this == s;
}

bool My_String::operator>=(const My_String& s) const
{
	return !(*this < s._str);
}

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

bool My_String::operator!=(const My_String& s) const
{
	return !(*this == s._str);
}

6.流插入和提取

void My_String::clear()
{
	_str[0] = '\0';
	_size = 0;
}
ostream& operator<<(ostream& out,  My_String& string)
{
	out << string._str;
	return out;
}
istream& operator>>(istream& in, My_String& string)
{
	string.clear();
	char ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		string += ch;
		ch = in.get();

	}
	return in;
}

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

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

相关文章

【LeetCode】每日一题 2024_9_21 边积分最高的节点(哈希)

前言 每天和你一起刷 LeetCode 每日一题~ LeetCode 启动&#xff01; 题目&#xff1a;边积分最高的节点 代码与解题思路 func edgeScore(edges []int) (ans int) {// 直接维护哈希最大值即可mp : map[int]int{}for i, v : range edges {mp[v] i// 如果多个节点的 边积分 相…

Flutter中使用FFI的方式链接C/C++的so库(harmonyos)

Flutter中使用FFI的方式链接C/C库&#xff08;harmonyos&#xff09; FFI plugin创建和so的配置FFI插件对so库的使用 FFI plugin创建和so的配置 首先我们可以根据下面的链接生成FFI plugin插件&#xff1a;开发FFI plugin插件 然后在主项目中pubspec.yaml 添加插件的依赖路径&…

PDF 秒变 JPG,2024 这些工具来助力

有些扫描仪默认将扫描文档保存为PDF格式&#xff0c;若事先未加留意&#xff0c;便可能累积大量PDF文件。然而&#xff0c;在需要将这些文件插入到其他文档或进行图形设计时&#xff0c;PDF格式可能会显得不够灵活或便捷。这时&#xff0c;将PDF转换为JPG图片格式就成为了一个实…

9.C++程序中的选择语句

选择语句一共分为两种&#xff1a;条件语句和开关语句 其中条件语句叫if语句&#xff0c;常见的形式为&#xff1a;if ... else ... ; 再复杂一些为if... else if ... else ... ; 开关语句又叫switch语句&#xff0c;类型于开关的使用形式常见的有 switch (var) case : ... b…

ai写论文哪个软件好?分享4款ai论文写作工具软件

在当前的学术研究和论文写作领域&#xff0c;AI技术的应用已经成为一种趋势。AI论文写作工具不仅能够提高写作效率&#xff0c;还能帮助研究者生成高质量的论文。以下是四款值得推荐的AI论文写作工具软件&#xff0c;其中特别推荐千笔-AIPassPaper。 1. 千笔-AIPassPaper 传送…

Cluade 3.5 Sonnet 提示词泄露

prompt 翻译&#xff1a; The notebook currently demonstrates support for a two agent setup. Support for GroupChat is currently in development.

EF Core优化技巧之预热处理

1. 前言 今天和听到同事们在讨论一个关于使用EFCore时&#xff0c;为什么第一次查询数据库总是很慢的原因。我们在工作中经常使用EFCore进行数据访问&#xff0c;但发现每次第一次查询都需要较长的时间&#xff0c;这给我们带来了困扰。因此&#xff0c;我们聚在一起&#xff…

Redis实战--Redis应用过程中出现的热门问题及其解决方案

Redis作为一种高性能的key-value数据库&#xff0c;广泛应用于缓存、消息队列、排行榜等场景。然而&#xff0c;在实际应用中&#xff0c;随着业务规模的不断扩大和访问量的持续增长&#xff0c;缓存系统也面临着诸多挑战&#xff0c;其中最为典型的便是缓存穿透、缓存击穿和缓…

Ansible集群服务部署案例

案例描述 本案例共讲述了多个节点部署Elk集群日志分析系统&#xff0c;分别在三个节点使用ansible部署Kibana、Logstash以及Elasticsearch服务。 案例准备 1. 规划节点 IP 主机名 节点 192.168.100.25 ansible Ansible节点 192.168.100.35 node1 Elasticsearch/Kiba…

shell用文件管理备份文件路径

从文件中读取文件路径 while IFS read -r s; do&#xff1a;这行代码启动一个 while 循环&#xff0c;逐行读取 001.sh 文件的内容。 IFS&#xff1a;将内部字段分隔符&#xff08;IFS&#xff09;设置为空&#xff0c;确保读取整行而不被默认的空格和换行符分隔。这样可以保…

知识管理数据库

知识管理数据库&#xff0c;可以分为几类&#xff1a; 灵感库、卡片库、作品库。 灵感库&#xff0c;通常是素材&#xff0c;想法。 片库&#xff0c;是完整的&#xff0c;成段落的文字。 作品库&#xff0c;是文章、专栏&#xff0c;或者书籍。 这三者的关系&#xff0c;好比…

java通过webhook给飞书发送群消息

现在使用飞书的人越来越多了&#xff0c;飞书有一个最大的好处&#xff0c;可以使用webhook简便的发送群消息。而在工作中&#xff0c;也经常会因为一些运维方面的工作&#xff0c;需要给飞书发送群消息&#xff0c;来实时提醒相关负责人,及时处理工作。 一、先看一下效果吧&a…

【计算机网络】网络层详解

文章目录 一、引言二、IP 基础知识1、IP 地址2、路由3、IP报文4、IP报文的分片与重组 三、IP 属于面向无连接型四、IP协议相关技术1、DNS2、ICMP3、NAT技术4、DHCP 一、引言 TCP/IP的心脏是网络层。这一层主要由 IP 和 ICMP 两个协议组成。网络层的主要作用是“实现终端节点之…

Visual Studio Code下载安装及汉化

官网&#xff1a;https://code.visualstudio.com/ 按照指示一步步操作即可&#xff1a; 汉化&#xff1a;

Stable Diffusion零基础学习

Stable Diffusion学习笔记TOP12 _插件篇之ControlNet功能篇 ControlNet目前支持的10多种预处理器&#xff0c;根据数据检测种类可分为两种类型&#xff1a; 1、功能型&#xff1a;拥有着不同的能力 2、构图型&#xff1a;控制着SD扩散图形的构图规则 Seg语义分割&#xff1a…

2025年第十届智能信息技术国际会议 (ICIIT 2025)即将召开!

第10届智能信息技术国际会议&#xff08;ICIIT 2025&#xff09;将于2025年2月20日至23日在越南河内举行。ICIIT系列会议将每年举行&#xff0c;为智能信息技术及相关领域提供互动论坛&#xff0c;除了越南的研究人员外&#xff0c;会议小组还欢迎来自世界各地的有兴趣与该地区…

SpringBoot框架快速入门

1、起步依赖 1.概述 在入门案例中&#xff0c;我们引入了web依赖和test依赖&#xff0c;这两个依赖是SpringBoot中的starter依赖&#xff0c;starter依赖也被称为起步依赖 SpringBoot 在配置上相比Spring要简单许多&#xff0c;其核心在于spring-boot-starter&#xff0c;在使…

Django Web开发基础介绍

概述 Django 是后端Python的 Web 开发框架&#xff0c;主要任务是处理与服务器和数据库相关的事务&#xff0c;模板渲染动态生成网页内容只是其中一部分。 Django 内置了的模板引擎&#xff0c;可以将 HTML 页面与 Python 代码进行分离。模板引擎提供了丰富的模板标签和过滤器…

???Ansible-使用roles

文章目录 一、Ansible的内置的或官方推荐创建的目录及文件介绍roles目录解释1、roles/自定义角色名目录下2、roles/自定义角色名目录/tasks目录下3、roles/自定义角色名目录/handlers目录下4、roles/自定义角色名目录/templates目录下5、roles/自定义项目名目录/files目录下6、…

vscode【实用插件】Notes 便捷做笔记

安装 在 vscode 插件市场的搜索 Notes点 安装 安装成功后&#xff0c;vscode 左侧栏会出现 使用 初次使用 需先选择一个本地目录 重启 vscode 后&#xff0c;得到 切换笔记目录 新建笔记 快捷键为 Alt N 默认会创建 .md 文件 配合插件 Markdown Preview Enhanced 预览 .md…