C++——模拟实现string

1.再谈string

string为什么要被设计成模板?日常使用string好像都是char*,char*不够使用吗,为什么要设计成模板呢?

1.1 关于编码

//计算机的存储如何区分呢?int main()
{//比如在C语言中,有整型//如果是有符号的话,设计了原、反、补码//负数要存储它的补码,方便运算//浮点数要设计到存精度等问题int a = 0;return 0;
}

那么如何去更好地表示其它的各种文字呢?

1.2 ASCII编码

ASCII_百度百科 (baidu.com)

ASCII编码表示美国信息技术的编码,在一开始设计时,只考虑了存储美国的文字信息

内存中存储字母时,不会把字母存储进去,内存中都是0、1组合的二进制代码,所以,要在二进制代码代表的值符号间,建立一个一一映射对应的关系,这个关系表,一般称为编码表


ASCII表就是美国的文字所建立的拥有一一映射对应的关系的编码表

int main()
{	char str[] = "hello world";return 0;
}//要显式时,发现它是字符串,会去查它的编码表

1.3 unicode

统一码_百度百科 (baidu.com)

计算机向全世界推广后,不单单需要能够表示汉字,还需要能够表示其他国家的文字符号,有些国家的文字比较简单,有些可能比汉字还要复杂。因此,设计出了万国码。

所以要表示汉字,也要使得每个汉字都对应一个数字
那如何表示汉字呢?也是用一个byte表示一个汉字吗?
要知道1个byte才8个bit位,这样的话才只能表示256个汉字,显然不能很好地表示

方案有三种

1.3.1 UTF-8

int main()
{//UTF-8   主流使用,一个值可能在多个字节上面存储//但windows喜欢使用gbk,linux喜欢使用UTF-8char str1[] = "hello world";//12bytechar str2[] = "工大";//5bytechar str3[] = "工大 hello";//可以混着使用,11bytecout << sizeof(str1) << endl;cout << sizeof(str2) << endl;cout << sizeof(str3) << endl;char* a = str3;cout << *a << endl;return 0;
}

UTF-8的缺点:变长,意味着识别比较复杂,太不统一,比如做屏蔽时;
或是有些情况下文字不需要兼容ASCII,这时就要使用其它方式了

1.3.2 UTF-16

1.3.3 UTF-32

1.4设计成模板的原因

int main()
{//为了更好地兼容这两种编码,类型进行了延申//C++11之前,设计出了宽字节,一个char占据2byte//用来更好地表示其它的编码,比如UTF-16wchar_t ch1;	//宽字节cout << sizeof(ch1) << endl;//2bytechar16_t ch2;char32_t ch3;cout << sizeof(ch2) << endl;//2bytecout << sizeof(ch3) << endl;//4bytereturn 0;
}

这也是string要设计成模板的原因,它可以传不同的模板参数,比如char16_t、char32_t等,可以适用不同的编码,表示更多国家的不同的文字。

1.5何为乱码

比如某文字默认使用UTF-8来存储,但显示时没有使用对于的编码表来查找,就会出现乱码。
存储格式和解释方式没有对应起来

1.6GBK

GBK字库_百度百科 (baidu.com)

虽然unicode是全世界的编码,但它未必非常适合汉字的表达。GBK是中国创造的、更适合汉字表达的编码表。

int main()
{char s1[] = "你好!!";s1[0]++;//陪s1[0]++;s1[0]++;s1[0]++;s1[3]++;//耗s1[3]++;s1[3]++;//号//在净网行动中非常有用//黑名单词库return 0;
}

2.模拟实现string

2.1无参的构造和析构

//string.h#pragma oncenamespace jxy
{class string{public://初始化的顺序是按照声明的顺序,而不是初始化列表出现的顺序string(const char* str):_size(strlen(str)),_capacity(_size)//capacity一般不算'\0'{_str = new char[_capacity+1];//开空间时要多开一个strcpy(_str, str);//strcpy会把'\0'也拷贝过去}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity;};void test_string1(){string s1("hello world");cout << s1.c_str() << endl;}
}//Test.cpp#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
//这里间接包了string的相关函数//#include"string.h"
//注意,如果写在这里可能会报错,因为std的展开在下面
//编译器为了追求编译速度,只会向上查找using namespace std;
#include"string.h"//编译器实际编译时是没有.h的,在预处理阶段就在.c或者.cpp展开了int main()
{jxy::test_string1();return 0;
}

2.2单参数的构造

错误示范:

namespace jxy
{class string{public:string():_str(nullptr),_size(0),_capacity(0){}};void test_string1(){string s2;cout << s2.c_str() << endl;//这样会崩溃//char*有个特点,自定义类型识别会以字符串去识别,会直接去解引用,遇到'\0'才终止//这里空指针解引用就会出错std::string s1;cout << s1.c_str() << endl;//库里面的是没问题的}
}

正确写法1:

namespace jxy
{class string{public:string():_str(new char[1]{'\0'})//这里需要开空间,_size(0),_capacity(0){//_str[0] = '\0';//或者在这里初始化}};void test_string1(){string s2;cout << s2.c_str() << endl;}
}

正确写法2:在无参构造处给上缺省参数

namespace jxy
{class string{public://const char* str="\0" 这样写不够规范string(const char* str="")//C语言规定常量字符串后面默认有'\0':_size(strlen(str)),_capacity(_size){_str = new char[_capacity+1];strcpy(_str, str);}};void test_string1(){string s2;cout << s2.c_str() << endl;}
}

2.3 size、capacity和c_str

namespace jxy
{class string{public:size_t capacity() const{return _capacity;}size_t size() const{return _size;}const char* c_str() const{return _str;}};private:char* _str;size_t _size;size_t _capacity;
}

2.4遍历数组

2.4.1[]

namespace jxy
{class string{public:char& operator[](size_t pos){assert(pos < _size);return _str[pos];}const char& operator[](size_t pos) const//给const对象使用{assert(pos < _size);return _str[pos];}};
//...
}

2.4.2迭代器

namespace jxy
{class string{public:typedef char* iterator;//迭代器可以实现成指针,也可以不//可以使用原生指针来代替iteratoriterator begin(){return _str;}iterator end(){return _str+_size;}};
//...
}

2.4.3范围for

namespace jxy
{class string{//...};void test_string2(){//1. []string s1("hello world");for (size_t i = 0; i <s1.size() ; i++){cout << s1[i] << " ";}cout << endl;//2.迭代器string::iterator it=s1.begin();//在里面typedef的或者是内部类while (it != s1.end()){cout << *it << " ";it++;}cout << endl;//3.范围for//这里还支持范围forfor (auto ch : s1)//底层代码和上面的迭代器类似{//auto ch = *it;cout << ch << " ";}cout << endl;//范围for的本质是替换成迭代器,编译时直接替换过去//而且有很严格的规范,名字变化一下都是不可以的}
}

2.5复用reserve实现尾插

实现尾插的三种方式:

1.push_back

2.append

3.+=

namespace jxy
{class string{public:void reserve(size_t n){assert(n > _capacity);char* str1 = new char[n+1];//失败会抛异常strcpy(str1,_str);//会把'\0'拷贝过去delete[] _str;//越界会在这里崩溃_str = str1;_capacity = n;}void push_back(char ch){//扩容问题if (_size == _capacity){reserve(_capacity==0 ? 4 :_capacity * 2);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}string& operator+=(char ch){push_back(ch);return *this;}string& operator+=(const char* str){append(str);return *this;}
//...};void test_string3(){string s1("hello world");cout << s1.c_str() << endl;s1.push_back('0');cout << s1.c_str() << endl;s1.append("hello lxy");cout << s1.c_str() << endl;s1 += '$';cout << s1.c_str() << endl;s1+="hellox";cout << s1.c_str() << endl;}
}

2.6 insert和erase

insert错误写法:

namespace jxy
{class string{public:void insert(size_t pos,char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size;//头插会出问题,这里是无符号数//int end = _size; //改为有符号数也是行不通的//在操作符,如 >= 两边,类型不同时,会发生类型提升//这里end虽然是有符号数,但会被提升成无符号数while (end >= pos){_str[end + 1] = _str[end];end--;}_str[pos] = ch;_size++;}
//...};void test_string4(){string s1("hello world");cout << s1.c_str() << endl;s1.insert(5, '#');cout << s1.c_str() << endl;s1.insert(s1.size(), '#');cout << s1.c_str() << endl;s1.insert(0, '#');//头插cout << s1.c_str() << endl;}}

正确写法1:

namespace jxy
{class string{public:void 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++;}
//...};
}

正确写法2:

namespace jxy
{class string{public:void insert(size_t pos,char ch){assert(pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : _capacity * 2);}int end = _size;while (end>=(int)pos)//或者把这里强转一下,避免出现类型提升{_str[end] = _str[end-1];end--;}_str[pos] = ch;_size++;}
//...};
}

namespace jxy
{class string{public:void insert(size_t pos,size_t len=npos){}void insert(size_t pos,const char* str){}void erase(size_t pos,size_t len=npos){}private:char* _str;size_t _size;size_t _capacity;//注意//const static size_t npos =-1;  //特例//这里是特殊情况,按理说不能在这里初始化//这里给值给的是缺省值,缺省值是给初始化列表使用的//静态成员变量不会去执行初始化列表,它不属于对象,它属于整个类,按理说要在类外面初始化//但是const修饰的静态的整型可以,所以这里既是定义,又是定义初始化//const static double npos = 1.2;//这样都不会去支持const static size_t npos;};const size_t string::npos = -1;
}

2.7运算符重载

namespace jxy
{class string{public://运算符重载
//设计成非成员函数更好,便于模板的使用bool operator<(const string& str1){return strcmp(_str, str1._str)<0;}bool operator==(const string& str1){return strcmp(_str, str1._str)==0;}bool operator<=(const string& str1){return *this<str1 || *this==str1;}bool operator>=(const string& str1){return !(*this<str1);}bool operator>(const string& str1){return !(*this<=str1);}bool operator!=(const string& str1){return !(*this == str1);}
//...};
}

2.8流插入

namespace jxy
{class string{public:typedef const char* const_iterator;//const迭代器指向的内容不能修改//指针本身可以修改,指向的内容不能修改const_iterator begin() const{return _str;}const_iterator end() const{return _str + _size;}//...};//没有必要定义为友元,想访问私有成员变量才需要定义为友元ostream& operator<<(ostream& out, const string& s){1.//for (size_t i = 0; i < s.size(); i++)//{//	out << s[i];//}//return out;//2.可以使用范围for,但要替换成const迭代器for (auto ch : s)out << ch;return out;}
}

2.9流提取和clear

namespace jxy
{class string{public:void clear(){_str[0] = '\0';_size = 0;}//...};//没有必要定义为友元,想访问私有成员变量才需要定义为友元istream& operator>>(istream& in,string& s){//流提取要从缓冲区中取一个个的字符s.clear();char ch;//in >> ch; //这样写获取不到空格ch = in.get();while (ch != ' ' && ch != '\n'){s += ch;//in >> ch;ch = in.get();}return in;}void test_string6(){string s1("hello world");cout << s1 << endl;cin >> s1;cout << s1 << endl;}
}

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

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

相关文章

msvcp140.dll0丢失的解决方法,总结6种靠谱的解决方法

再使用计算机的过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。这个问题可能会影响到我们的正常使用&#xff0c;因此需要及时解决。经过一段时间的学习和实践&#xff0c;我总结了以下六种靠谱的解决方法&#xff0c;希望对大家…

Spring的任务调度

Spring的任务调度 1.概述 Spring框架为任务调度提供了专门的解决方案。在Spring框架的org.springframework.scheduling包中&#xff0c;通过对JDK 的ScheduledExecutorService接口的实例进行封装&#xff0c;对外提供了一些注解和接口&#xff0c;为开发者处理定时任务提供了…

STM32F407单片机编程入门(十二) FreeRTOS实时操作系统详解及实战含源码

文章目录 一.概要二.什么是实时操作系统三.FreeRTOS的特性四.FreeRTOS的任务详解1.任务函数定义2.任务的创建3.任务的调度原理 五.CubeMX配置一个FreeRTOS例程1.硬件准备2.创建工程3.调试FreeRTOS任务调度 六.CubeMX工程源代码下载七.小结 一.概要 FreeRTOS是一个迷你的实时操…

联想(lenovo) 小新Pro13锐龙版(新机整理、查看硬件配置和系统版本、无线网络问题、windows可选功能)

新机整理 小新pro13win10新机整理 查看硬件配置和系统版本 设置-》系统-》系统信息 无线网络问题 部分热点可以&#xff0c;部分不可以 问题&#xff1a;是因为自己修改了WLAN的IP分配方式为手动分配&#xff0c;导致只能在连接家里无线网的时候可以&#xff0c;连接其他…

统信UOS的「端侧模型」

统信UOS早有布局的「端侧模型」的相关信息。 文章目录 前言一、究其原因1. 从需求侧来看2. 从技术侧来看二、产品特色1. 更流畅的使用体验2. 更智能的加速框架3. 更包容的硬件策略前言 在苹果2024秋季新品发布会上,苹果发布了有史以来最大的iPhone。而同一天开发布会的华为,…

【Linux】POSIX信号量、基于环形队列实现的生产者消费者模型

目录 一、POSIX信号量概述 信号量的基本概念 信号量在临界区的作用 与互斥锁的比较 信号量的原理 信号量的优势 二、信号量的操作 1、初始化信号量&#xff1a;sem_init 2、信号量申请&#xff08;P操作&#xff09;&#xff1a;sem_wait 3、信号量的释放&#xff08…

机器之心 | 阿里云Qwen2.5发布!再登开源大模型王座,Qwen-Max性能逼近GPT-4o

本文来源公众号“机器之心”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;阿里云Qwen2.5发布&#xff01;再登开源大模型王座&#xff0c;Qwen-Max性能逼近GPT-4o 人工智能领域再度迎来重磅消息&#xff01; 2023 年 8 月&#…

尚品汇-H5移动端整合系统(五十五)

目录&#xff1a; &#xff08;1&#xff09;运行前端页面 &#xff08;2&#xff09;启动前端页面 &#xff08;3&#xff09;添加搜索分类接口 &#xff08;4&#xff09;购物车模块修改 &#xff08;5&#xff09;登录模块 &#xff08;6&#xff09;订单模块 &#…

【巧用ddddocr破解算术运算验证码的经典示范】

计算型验证码 算术验证码&#xff0c;也叫计算型验证码, 计算型验证码其实是一种特殊的字符型验证码&#xff0c;只不过在它的基础上增加了数字运算。   计算型验证码在将人类视觉和计算机视觉的差异作为区分用户和电脑的依据的同时&#xff0c;还加上了逻辑运算&#xff0c…

基于SpringBoot+Vue的在线酒店预订系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

人工智能开发实战常用分类算法归纳与解析

内容导读 决策树贝叶斯分类器最近邻分类器支持向量机神经网络 一、决策树 决策树(Decision Tree)是用于决策的一棵树&#xff0c;从根节点出发&#xff0c;通过决策节点对样本的不同特征属性进行划分&#xff0c;按照结果进入不同的分支&#xff0c;最终达到某一叶子节点&am…

计算机毕业设计 基于Python的校园个人闲置物品换购平台 闲置物品交易平台 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

深耕电通二十年,崔光荣升电通中国首席执行官

电通今日宣布&#xff0c;任命拥有二十年深厚电通工作经验的杰出行业领袖崔光(Guang Cui)为电通中国首席执行官&#xff0c;该任命自2024年9月27日起生效。崔光自2004年加入电通以来&#xff0c;从策略规划岗位逐步成长为公司的核心领导者&#xff0c;这也是他职业生涯中的第9次…

篮球运动场景物体检测系统源码分享

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

Linux基础---13三剑客及正则表达式

一.划水阶段 首先我们先来一个三剑客与正则表达式混合使用的简单示例&#xff0c;大致了解是个啥玩意儿。下面我来演示一下如何查询登录失败的ip地址及次数。 1.首先&#xff0c;进入到 /var/log目录下 cd /var/log效果如下 2.最后&#xff0c;输入如下指令即可查看&#xf…

OpenGL渲染管线(Rendering Pipeline)介绍

渲染管线 计算机图形学中&#xff0c;计算机图形管线&#xff08;渲染管线 或简称 图形管线、流水线&#xff09;是一个概念模型&#xff0c;它描述了t图像系统将 3D场景渲染到2D屏幕所需执行的一系列步骤。渲染管线大的可以分为三个阶段。 &#xff08;一&#xff09;应用阶段…

[UTCTF2020]sstv

用goldwave和010editor打开均未发现线索&#xff0c; 网上搜索sstv&#xff0c;豆包回答如下&#xff1a; 慢扫描电视&#xff08;Slow Scan Television&#xff0c;简称 SSTV&#xff09;是一种通过无线电传输和接收静态图像的技术。 一、工作原理 SSTV 通过将图像逐行扫描并…

【GMNER】Grounded Multimodal Named Entity Recognition on Social Media

Grounded Multimodal Named Entity Recognition on Social Media 动机解决方法特征抽取多模态索引设计索引生成框架EncoderDecoder 实体定位、实体-类型-区域三元组重建 出处&#xff1a;ACL2023 论文链接&#xff1a;https://aclanthology.org/2023.acl-long.508.pdf code链接…

[Linux] Linux操作系统 进程的状态

标题&#xff1a;[Linux] Linux操作系统 进程的状态 个人主页&#xff1a;水墨不写bug &#xff08;图片来源于网络&#xff09; 目录 一、前置概念的理解 1.并行和并发 2.时间片 3.进程间具有独立性 4.等待的本质 正文开始&#xff1a; 在校的时候&#xff0c;你一定学过《…

10 张手绘图详解Java 优先级队列PriorityQueue

PriorityQueue 是 Java 中的一个基于优先级堆的优先队列实现&#xff0c;它能够在 O(log n) 的时间复杂度内实现元素的插入和删除操作&#xff0c;并且能够自动维护队列中元素的优先级顺序。 通俗来说&#xff0c;PriorityQueue 就是一个队列&#xff0c;但是它不是先进先出的…