当前位置: 首页 > news >正文

[C++]string类模拟实现

目录

前言:

1. string框架构造

2. 默认函数

2.1 构造函数

2.2 析构函数

2.3 拷贝构造

2.4 赋值重载

3. 迭代器

 4. 整体程序


前言:

        本篇文章模拟实现了C++中string的部分功能,有助于大家了解和熟悉string类,虽然这个类不难实现,但是其中有些小细节还是可以细细品味的。

1. string框架构造

        我们得知道编写一个类的首要步骤不是直接开写代码,而是需要思考这个类需要用什么样的数据结构?需要准备哪些参数,这个类的作用域在哪里等等内容。

        首先,咱们知道string类是有多个版本的,不同版本下的string类的变量是不同的,就我所知,在vs下string的实现是下方的格式,实际上比我写的要复杂,但是能够表示。

class string{
 private:

        size_t _size;

        size_t _capacity;

        char*  _buf;

        char   _arr[16];

};

        这样做的用处主要是空间换取时间,当我们只是一个小字符串,那么就直接将数据存到了_arr的数组当中,因为是直接写在类里面的数组,所以就省去了向堆申请空间这一过程,如果数据大于了_arr数组大小,那么系统才会向堆申请空间。所以在vs下的string类有28个字节。

        在Linux下则不同,string类有8个字节,结构如下:至于为什么是8个字节,那是因为Linux下跑C++代码默认是64位机哦,我现在还不会改为32位运行,请原谅博主。

class string{

private:

        m_string* _point;

}

         其中m_string是一个自定义类型,里面存了和vs下差不多样式的变量,只不过没有数组的存在,如下:

class m_string{

        size_t _size;

        size_t _capacity;

        char* _buf;

};

         这样做是因为Linux有一个特性,那就是赌,它赌我们在拷贝时不需要更改,不改那么他就直接将指针指向这个空间,一旦需要更改,那他就会触发写时拷贝这个牛技术。如下图:

        对此我只能是表示,这些大佬们写代码是真牛哇,为了提升代码效率都能卷成这样了,我们自己的实现就不用这么高级的操作了,太难写了。既然是为了理解string类,所以我决定用下方的结构:(简单清楚好写)

class string{   

 private:
        char* _buf;
        size_t _size;
        size_t _capacity;
};

        还有一点,我们的这个类尽量写在一个自己的命名空间当中,防止与库中的string起冲突。

如下:

namespace yf
{class string{   private:char* _buf;size_t _size;size_t _capacity;};
}

2. 默认函数

2.1 构造函数

        先看一下C++库中提供的构造函数:

        真是有够恐怖的,这提供的接口有点猛,不过我们呢也不需要写这么多,写几个常用的就好。还有就是这些接口设计得有一些不合理,比如说default和from c-string两个函数很明显是可以写为一个函数的,以缺省参数来表示的,不过也能理解,毕竟C++是为了兼容C的。

代码:

//构造函数string(const char* str = ""):_size(strlen(str))
{_capacity = _size;			_buf = new char[_capacity + 1];strcpy(_buf, str);
}

        上方我讲解了可以用缺省参数来合并两个函数接口,但是大家再不看我的代码之前能想出来吗?除此之外,我还想了两个方法,一个是 ‘ ’,另一个是“\0”,有人看出不对了吗,这两个方式都是不行的,第二种倒也不是不行,而是太多余了,本身“”里面就已经有了一个‘\0’了显得我们不够专业,那这可不行。第一种用字符方式缺省那是真的错得离谱,我们之后可是要用字符串呢,用字符接收?这很明显不行哇,所以这种情况是一定要避免的。不然以后去公司面试,让我们写个类都乱写,简历上还写着熟悉C++,这不扯淡嘛。还有一点,为了保持new空间和delete保持一致,

        然后我们的构造函数当中还用了初始化列表这一概念,通过我们传入的字符串长度去初始化_size,然后_capacity又在下方被赋值,再为_buf向堆上申请_capacity+1个字节大小的空间,为什么加1,当然是为了存我们的'\0'哇,小笨蛋,最后再使用strcpy函数将字符拷贝到我们的空间当中。

 测试用例:

void test1()
{yf::string str1;yf::string str2("hello");yf::string str3("good morning xxxxxxxxxxxxx");
}

2.2 析构函数

        大伙们回忆一下,为什么我们要有析构函数?因为我们向堆空间申请了空间了,所以需要释放,避免造成内存泄漏的过程。

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

        看着这里释放内存的方式是delete[] _buf,也就与我们的构造函数对应起来了,如果构造函数的缺省参数是‘’的话,这里就不好析构,就得分情况,这不是脱了裤子放屁——多此一举嘛。

2.3 拷贝构造

        拷贝构造和构造差不多,我也就不多做解释了,只是要注意参数得用引用。

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

2.4 赋值重载

        如果是没有数据的重载,好,简单,但是呢,我的这个字符串本来是有数据的,另外被拷贝对象有大于的情况,有小于的情况,有等于的情况,如果拷贝错误怎么办?原来的数据这么办?这一些列的问题问下来还简单嘛?依然简单,只不过多数人在一时间想不到完全实现功能的代码罢了。

//赋值重载
string& operator=(const string& str)
{//不能自己拷贝自己if (this != &str){//当前容量大于被拷贝对象并不多时if (_capacity - str._capacity < 10){strcpy(_buf, str._buf);_capacity = str._capacity;_size = str._size;}else{//考虑到new失败的情况char* temp = new char[str._capacity + 1];strcpy(temp, str._buf);delete[] _buf;_capacity = str._capacity;_size = str._size;}}return *this;
}

         请看我的代码,首先我们不能直接赋值给自己,这样会让我们把数据给delete掉了,导致整个程序出BUG了还不知道,这是很危险的。然后呢,我还考虑到了,当当前对象的容量大于被拷贝对象,并且大于的值并不多时,可以直接拷贝,不用delete后再整个赋值,节省了时间。

        在下方需要delete的过程中,我们需要考虑到当new失败之后会怎么样,所以不能先释放掉原空间,需要创建一个变量去接收被拷贝对象的数据,如果new失败了,外部的try会直接接收到。

3. 迭代器

        说起string类等一系列库类,里面都是提供了迭代器的,我们也模仿着在我们的类里面实现迭代器,我们呢还是从简,用指针来实现迭代器,不过我们不能将迭代器理解为指针,只是指针能够实现。

定义:

typedef char* iterator;
typedef const char* const_iterator;

对应接口: 

//迭代器begin
iterator begin()
{return _buf;
}
const_iterator begin() const
{return _buf;
}//迭代器end
iterator end()
{return _buf + _size;
}const_iterator end() const
{return _buf + _size;
}

        此时,我们的string类就能实现范围for这个语法糖了,至于为什么能实现,我只能说,范围for底层就是迭代器。

测试:

void test2()
{const yf::string str1("hello world");yf::string::const_iterator it = str1.begin();while (it != str1.end()){cout << *it << " ";++it;}cout << endl;for (auto e : str1){cout << e << " ";}cout << endl;
}

 4. 整体程序

        其余的函数都是对于string类的补充,比如reserve、resize、+=、[]、<<、>>函数等等,博主很懒,并且认为这些大家都是有基础能实现的所以就不做讲解了,附下代码:

#pragma once#include<iostream>
#include<string>
#include<assert.h>
using std::cout;
using std::cin;
using std::endl;namespace yf
{class string{friend std::ostream& operator<<(std::ostream& out, const string& str);friend std::istream& operator>>(std::istream& in, yf::string& str);public:typedef char* iterator;typedef const char* const_iterator;//构造函数string(const char* str = ""):_size(strlen(str)){_capacity = _size;			_buf = new char[_capacity + 1];strcpy(_buf, str);}//拷贝构造string(const string& str){_capacity = str._capacity;_size = str._size;_buf = new char[str._capacity + 1];strcpy(_buf, str._buf);}//赋值重载string& operator=(const string& str){//不能自己拷贝自己if (this != &str){//当前容量大于被拷贝对象并不多时if (_capacity - str._capacity < 10){strcpy(_buf, str._buf);_capacity = str._capacity;_size = str._size;}else{//考虑到new失败的情况char* temp = new char[str._capacity + 1];strcpy(temp, str._buf);delete[] _buf;_capacity = str._capacity;_size = str._size;}}return *this;}char& operator[](size_t pos){assert(pos < _size);return _buf[pos];}const char& operator[](size_t pos) const{assert(pos < _size);return _buf[pos];}//tidy_capacityvoid reserve(size_t n);void resize(size_t n, char c = '\0');//addvoid push_back(char c);void append(const string& str);void append(const char* s);string& operator+=(char c){push_back(c);return *this;}string& operator+=(const string& str){append(str);return *this;}string& operator+=(const char* s){append(s);return *this;}//insertstring& insert(size_t pos, size_t n, char c);string& insert(size_t pos, const char* s);string& insert(size_t pos, const string& str);//迭代器beginiterator begin(){return _buf;}const_iterator begin() const{return _buf;}//迭代器enditerator end(){return _buf + _size;}const_iterator end() const{return _buf + _size;}//输出void print() const{cout << _buf << endl;}const char* c_str() const{return _buf;}//大小size_t size() const {return _size;}//容量size_t capacity() const{return _capacity;}//匹配字符size_t find_first_of(char c, size_t pos = 0){const_iterator it = begin();it += pos;while (it != end()){if (*it == c){return (it - begin());}it++;}return -1;}//获取字串string substr(size_t pos = 0, size_t len = -1) const;//析构~string(){delete[] _buf;_size = 0;_capacity = 0;}const size_t npos = -1;private:char* _buf;size_t _size;size_t _capacity;};
}
#define _CRT_SECURE_NO_WARNINGS 1#include"string.h"//tidy_capacity->reserve
void yf::string::reserve(size_t n)
{if (n > _capacity){char* temp = new char[n + 1];strcpy(temp, _buf);delete[] _buf;_buf = temp;_capacity = n;}
}//tidy_capacity->resize
void yf::string::resize(size_t n, char c)
{//长度小于_size时,需要删除n位置后的数据if (n < _size){_size = n;_buf[_size] = '\0';}else{//当n>_size有两种情况,大于capacity和小于capacityif (n > _capacity){//大于需要扩容操作reserve(n);					//复用reserve}//二者都有同样的需求,对后面的数据初始化为cwhile (_size != n){_buf[_size] = c;++_size;}_buf[_size] = '\0';}
}//add
void yf::string::push_back(char c)
{if (_size + 1 > _capacity){//加一预防_capacity初始为0reserve(2 * _capacity + 1);}_buf[_size++] = c;_buf[_size] = '\0';
}void yf::string::append(const string& str)
{if (_size + str._size > _capacity){reserve(_size + str._size);}size_t len = str._size;int i = 0;while (i!=len){_buf[_size++] = str[i];i++;}_buf[_size] = '\0';
}void yf::string::append(const char* s)
{size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}while (*s != '\0'){_buf[_size++] = *s;s++;}_buf[_size] = '\0';
}//insert 
yf::string& yf::string::insert(size_t pos, size_t n, char c)
{if (_size + n > _capacity){reserve(_size + n);}size_t end = _size + n;while (end - n + 1 > pos){_buf[end] = _buf[end - n];--end;}for (size_t i = 0; i < n;++i){_buf[pos + i] = c;}_size += n;return *this;
}//插入字符串
yf::string& yf::string::insert(size_t pos, const char* s)
{size_t len = strlen(s);if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end - len + 1 > pos){_buf[end] = _buf[end - len];--end;}strncpy(_buf + pos, s, len);_size += len;return *this;
}yf::string& yf::string::insert(size_t pos, const yf::string& str)
{size_t len = str._size;if (_size + len > _capacity){reserve(_size + len);}size_t end = _size + len;while (end - len + 1 > pos){_buf[end] = _buf[end - len];--end;}strncpy(_buf + pos, str._buf, len);_size += len;return *this;
}//获取子串
yf::string yf::string::substr(size_t pos, size_t len)  const
{yf::string::const_iterator it = begin();yf::string temp;it += pos;if (len > size())len = size();while (it != begin() + len){temp += *it;++it;}return temp;
}//流插入
std::ostream& yf::operator<<(std::ostream& out, const yf::string& s)
{for (auto ch : s){out << ch;}return out;
}//流提取
std::istream& yf::operator>>(std::istream& in, yf::string& s)
{//从缓冲区获取字符char ch = in.get();while (ch != '\n'){s += ch;//记录上一次位置,拿到下一个字符ch = in.get();}return in;
}

相关文章:

[C++]string类模拟实现

目录 前言&#xff1a; 1. string框架构造 2. 默认函数 2.1 构造函数 2.2 析构函数 2.3 拷贝构造 2.4 赋值重载 3. 迭代器 4. 整体程序 前言&#xff1a; 本篇文章模拟实现了C中string的部分功能&#xff0c;有助于大家了解和熟悉string类&#xff0c;虽然这个类不难实…...

一个更适合Java初学者的轻量级开发工具:BlueJ

Java是世界上最流行的编程语言之一&#xff0c;它被广泛用于从Web开发到移动应用的各种应用程序。大部分Java工程师主要是用IDEA、Eclipse为主&#xff0c;这两个开发工具由于有强大的能力&#xff0c;所以复杂度上就更高一些。如果您刚刚开始使用Java&#xff0c;或者您更适合…...

从程序员到项目组长,要经历六重修炼

最近和粉丝朋友们交流时发现&#xff0c;有很多刚刚开始做项目组长的朋友自我认可度非常低&#xff0c;感觉做组长之后天天打杂&#xff0c;技术也荒废了。领导天天找你要成果&#xff0c;下属天天找你说困难&#xff0c;你在中间受领导和下属的夹板气。时间久了&#xff0c;你…...

我的 System Verilog 学习记录(5)

、 引言 本文简单介绍 System Verilog 语言的 控制流。 前文链接&#xff1a; 我的 System Verilog 学习记录&#xff08;1&#xff09; 我的 System Verilog 学习记录&#xff08;2&#xff09; 我的 System Verilog 学习记录&#xff08;3&#xff09; 我的 System Ver…...

多芯片设计 Designing For Multiple Die

Why a system-level approach is essential, and why its so challenging作者&#xff1a;Ann MutschlerAnn Mutschler is executive editor at Semiconductor Engineering.将多个裸片或芯粒集成到一个封装中&#xff0c;与将它们放在同一硅片上有着很大的区别。在同一硅片上&a…...

2022年全国职业院校技能大赛(中职组)网络安全竞赛试题A(10)

目录 竞赛内容 模块A 基础设施设置与安全加固 一、项目和任务描述&#xff1a; 二、服务器环境说明 三、具体任务&#xff08;每个任务得分以电子答题卡为准&#xff09; A-1任务一 登录安全加固&#xff08;Windows, Linux&#xff09; 1.密码策略&#xff08;Windows, …...

数据结构-简介

目录 1、简介 2、作用 3、分类 4、实现分类 1、简介 数据结构指的是组织和存储数据的方法。它涉及到一系列的算法和原则&#xff0c;用来设计和实现不同种类的数据类型&#xff0c;如数组、链表、树、图等等。数据结构的目的是在计算机程序中有效地管理和操作数据&#xff…...

python装饰器及其用法

python装饰器是什么? Python装饰器是一种语法结构&#xff0c;它可以让开发者在不修改原函数的基础上&#xff0c;在函数的前后运行额外的代码&#xff0c;这些代码可以达到修改函数行为的目的。Python装饰器的实质是一个可调用的对象&#xff0c;它可以接收函数作为参数…...

Appium自动化测试之启动时跳过初始化设置

Appium每次启动时都会检查和安装Appium Settings&#xff0c;这是完全没有必要的&#xff0c;在首次使用Appium连接设备是Appium Settings便已经安装好。怎样跳过安装Appium Settings呢&#xff1f;之前的做法是修改appium中的源文件中的android-helpers.js实现&#xff0c;如M…...

JavaScript DOM【快速掌握知识点】

目录 DOM简介 获取元素 修改元素 添加和移除元素 事件处理 DOM简介 JavaScript DOM 是指 JavaScript 中的文档对象模型&#xff08;Document Object Model&#xff09;&#xff1b;它允许 JavaScript 与 HTML 页面交互&#xff0c;使开发者可以通过编程方式动态地修改网页…...

不需要高深技术,只需要Python:创建一个可定制的HTTP服务器!

目录 1、编写服务端代码,命名为httpserver.py文件。 2、编写网页htmlcss文件&#xff0c;命名为index.html和style.css文件。 3、复制htmlcss到服务端py文件同一文件夹下。 4、运行服务端程序。 5、浏览器中输入localhost:8080显示如下&#xff1a; 要编写一个简单的能发布…...

渗透测试常用浏览器插件汇总

1、shodan这个插件可以自动探测当前网站所属的国家、城市&#xff0c;解析IP地址以及开放的服务和端口&#xff0c;包括但不限于FTP、DNS、SSH或者其他服务等&#xff0c;属被动信息搜集中的一种。2、hackbar&#xff08;收费之后用Max Hackerbar代替&#xff09;这个插件可用于…...

社区1月月报|OceanBase 4.1 即将发版,哪些功能将会更新?

我们每个月都会和大家展开一次社区进展的汇报沟通会&#xff0c;希望通过更多的互动交流让OceanBase 开源社区更加透明&#xff0c;实现信息共享&#xff0c;也希望能营造更加轻松的氛围&#xff0c;为大家答疑解惑&#xff0c;让大家畅所欲言。如果您对我们的社区有任何建议&a…...

Javascript的API基本内容(二)

一、事件监听 结合 DOM 使用事件时&#xff0c;需要为 DOM 对象添加事件监听&#xff0c;等待事件发生&#xff08;触发&#xff09;时&#xff0c;便立即调用一个函数。 addEventListener 是 DOM 对象专门用来添加事件监听的方法&#xff0c;它的两个参数分别为【事件类型】和…...

ChatGPT热度“狂飙”,OceanBase也去找它唠了唠

最近互联网的关键字 非 ChatGPT 莫属 就是这个小东西 集唠嗑、提问、答疑、科普、写作于一体 让我看看哪个孤独的打工人 还没和 ChatGPT 聊上一聊 有人说 ChatGPT 这么智能 或将取代人类的工作 OceanBase 的小编表示不服气 于是&#xff0c;抱着好奇之心试了一试 对 …...

HTTP协议基础知识点扫盲;HTTPS协议及密码学基础

目录 一、Http协议的特性 二、http协议的请求 1.请求行第一行&#xff0c;包含三个信息&#xff1a;请求方式&#xff0c;url&#xff0c;http协议版本 2.请求头浏览器向服务器发送一些状态数据&#xff0c;标识数据等等 3.请求主体请求代理端项服务器端&#xff0c;发送的…...

【golang/go语言】Go语言之反射

本文参考了李文周的博客——Go语言基础之反射。 一、反射初识 1. 什么是反射 在计算机科学中&#xff0c;反射是指计算机程序在运行时&#xff08;run time&#xff09;可以访问、检测和修改它本身状态和行为的一种能力。用比喻来说&#xff0c;反射就是程序在运行的时候能够…...

Java+Swing+Mysql实现超市管理系统

一、系统介绍1.开发环境操作系统&#xff1a;Win10开发工具 &#xff1a;IDEA2018JDK版本&#xff1a;jdk1.8数据库&#xff1a;Mysql8.02.技术选型JavaSwingMysql3.功能模块4.系统功能1.系统登录登出管理员可以登录、退出系统2.商品信息管理管理员可以对商品信息进行查询、添加…...

华为OD机试题,用 Java 解【机器人走迷宫】问题

最近更新的博客 华为OD机试题,用 Java 解【停车场车辆统计】问题华为OD机试题,用 Java 解【字符串变换最小字符串】问题华为OD机试题,用 Java 解【计算最大乘积】问题华为OD机试题,用 Java 解【DNA 序列】问题华为OD机试 - 组成最大数(Java) | 机试题算法思路 【2023】使…...

软件测试基本概念

软件测试基本概念 1. 什么是软件测试 软件测试就是验证软件产品特性(功能, 界面, 兼容性, 性能…)是否符合用户的需求&#xff0c;同时软件测试不仅要测试系统是否做了其应该做的, 还需要测试系统是否未作其不应该做的。 2. 调试与测试 软件测试与调试的区别&#xff1a; …...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...