String模拟实现【C++】【STL】

String模拟实现【C++】【STL】

  • 构造函数
  • 拷贝构造
  • 赋值重载
  • 析构函数
  • <<赋值重载
  • 插入函数
  • reserve
    • append函数
    • push_back函数
  • earse函数
  • 完整代码
    • string.h
    • string.cpp

  STL中有两个属性capacity和size,capacity是真正STL容器的真正内存大小,size是STL容器中数据的大小,实现内存变长这个特性的时候,判断当size大小达到capacity时,进行扩容即可,就像水缸一样,capacity就是水缸能装的最大水容量,size就是水缸中水的多少。capacity很多人会不明白他的必要性,我初学时也不明白,现在想来就是提高了扩容时的一个效率,当有capacity的时候,扩容时还是在之前那块连续的内存上扩容,否则的话,每次扩容可能都要重新找一块足够的内存,然后copy原来数据到新内存空间上。

  为了区别STL的string和自己写的string,我们使用命名空间限定一下:

#pragma oncenamespace th
{class string{public:string(const char* str = "");string(const string&);string& operator=(const string&);~string();private:size_t _size;size_t _capacity;char* _str;};
}

  写一个类,必要的构造函数,析构函数,拷贝构造函数,赋值重载函数都已经声明完成,必要这个词用的不严谨,对于类中都是内置类型的属性数据,不用定义构造也可以,此时我们的string是变长的,需要随时扩容的,另外一些属性的初始化也是必要的,因此在这里是必要的。

构造函数

th::string::string(const char* str): _size(0), _capacity(0), _str(nullptr)
{int size = strlen(str);if (size != 0){_size = size;_capacity = _size + 1;_str = new char(_capacity);strcpy(_str, str);}
}

  strlen仅仅能得到字符串中字符的个数,但是不包含末尾的 ‘\0’,_size表示数据的数量,因此不必加上这1个,capacity是申请的内存数量,因此加上这一个。

拷贝构造

  拷贝构造就是一个已经存在的string对象拷贝给一个新的string对象,

th::string::string(const string& str)
{string temp(str._str);std::swap(_str, temp._str);std::swap(_size, temp._size);std::swap(_capacity, temp._capacity);
}

赋值重载

  赋值重载如下,唯一就是这个定义时函数头的编写需要注意:

th::string& th::string::operator=(string& str)
{_str = str._str;_size = str._size;;_capacity = str._capacity;return *this;
}

析构函数

  注意点就是_str析构了以后记得置为空,不然就成野指针了。

th::string::~string()
{delete _str;_str = nullptr;_size = 0;_capacity = 0;
}

  该有的都有了,赶紧测试一下吧:

#include <iostream>
#include "string.h"int main()
{th::string str("abcdefg");std::cout << str << std::endl;return 0;
}

在这里插入图片描述
  这么多报错!!丸辣!!
  报错在 <<,原来是这个运算符也需要重载才可以使用,为什么嘞,因为我们cout的是自定义类型,里面那么多属性,不知道打印哪一个呀,比如我们的string类里面就有_str, _size, _capacity。这么几个属性是无法确定打印那个的,所以需要赋值重载一下。

<<赋值重载

在这里插入图片描述

  又丸辣!怎么声明都不对!!
  哎哎哎,原来是参数的问题, ==>>==是一个二元运算符,需要从>>的左右传入实参进入重载函数中作为形参,但是将重载函数放入类中作为类的一份子,会自动的往函数中添加一个this指针作为参数,所以此时函数拥有3个参数,这对于一个2元运算符来说是不对滴。因此:
在这里插入图片描述
  把它从类里面拿出来。
在这里插入图片描述
  想使用范围for发现需要begin迭代器函数,那么就定义。迭代器的意义是统一了所有容器的访问方式,并不是所有的容器的内存都是连续的,可以顺序访问,可以随机访问,迭代器完善了这一点。

char* th::string::begin()
{return _str;
}
char* th::string::end()
{return _str + _size;
}

在这里插入图片描述
  原因是因为我们的重载函数的第二个参数是被const修饰的,不希望修改str,所以需要const修饰的begin函数,如果将const去除,就不会报错了:
在这里插入图片描述
  当然没有必要,我们两个都提供:

char* th::string::begin() const
{return _str;
}
char* th::string::end() const
{return _str + _size;
}

  于是,重载函数为:

std::ostream& operator>>(std::ostream& out, const th::string& str)
{for (auto e : str){out << e;}return out;}

测试一下:
在这里插入图片描述
这跟出现scanf是不安全的一个意思,vs的问题,直接加上宏定义:
在这里插入图片描述
再次运行:
在这里插入图片描述
出现了这样的报错,堆溢出的问题,原因不止,调试一下:
从头开始:
在这里插入图片描述
  发现走到return报的错,return之后需要进行全局对象的析构,main函数内对象的析构,于是将端点打到析构函数中:
在这里插入图片描述
  发现delete的时候报错的。delete和new对应,回看了一眼new,发现一个基础错误:
在这里插入图片描述
  丸辣!!char后面怎么会用(),这是初始化啊,改成[]后:
在这里插入图片描述
析构函数也要改:
在这里插入图片描述
在这里插入图片描述
没毛病,运行成功。

测试拷贝构造函数:
在这里插入图片描述
运行成功。

测试赋值重载:
在这里插入图片描述
运行成功。

char& th::string::operator[](int index)
{assert(index >= 0 && index < _size);return *(_str + index);
}

插入函数

void th::string::insert(int index, char ch)
{assert(index >= 0 && index <= _size);for (int i = _size; i > index; i--){_str[i] = _str[i - 1];}_str[index] = ch;_size++;
}

在这里插入图片描述
测试发现没问题,再次进行插入发现报错:
在这里插入图片描述
原因是当空间不够的时候,没有自动扩容,为此我们先实现一个扩容函数reserve:

reserve

void th::string::reserve(size_t n)
{if (n > _capacity){char* newStr = new char[n];strcpy(newStr, _str);delete[] _str;_str = newStr;_capacity = n;}
}

当n < capacity 时就是缩容了,意义不大,拒绝实现。
在这里插入图片描述

void th::string::insert(int index, char ch)
{assert(index >= 0 && index <= _size);if (_size == _capacity){size_t newCapacity = _capacity == 0 ? INITIAL_CAPACITY : _capacity * EXPANSION_FACTOR;reserve(newCapacity);}for (int i = _size; i > index; i--){_str[i] = _str[i - 1];}_str[index] = ch;_size++;
}

测试一下:
在这里插入图片描述
  这个错误还是new出来的内存使用的时候溢出使用了不属于它的内存空间,在delete的时候就会出现这个错误,在代码中的体现就是,当_size = capacity的时候进行扩容,但是此时字符串末尾的’\0’字符进行溢出分配的内存空间了,因此当我们delete的时候就会报错,我们进行改正:

void th::string::insert(int index, char ch)
{assert(index >= 0 && index <= _size);if (_size+1 == _capacity){size_t newCapacity = _capacity == 0 ? INITIAL_CAPACITY : _capacity * EXPANSION_FACTOR;reserve(newCapacity);}for (int i = _size-1; i >= index; i--){_str[i+1] = _str[i];}_str[index] = ch;_size++;
}void th::string::reserve(size_t n)
{if (n > _capacity){_capacity = n;char* tmp = new char[_capacity];strcpy(tmp, _str);delete[] _str;_str = tmp;}
}

在这里插入图片描述
此时1万次插入都不会有问题。

append函数

void th::string::append(const char* str)
{if (_size + 1 + strlen(str) >= _capacity){size_t newCapacity = (_size + 1 + strlen(str)) * EXPANSION_FACTOR;reserve(newCapacity);}strcpy(_str + _size, str);_size += strlen(str);
}

测试没问题:
在这里插入图片描述

push_back函数

void th::string::push_back(char ch)
{if (_size + 2 >= _capacity){size_t newCapacity = (_size + 2) * EXPANSION_FACTOR;reserve(newCapacity);}_str[_size] = ch;_str[_size + 1] = '\0';_size++;
}

在这里插入图片描述

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

earse函数

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

测试:
在这里插入图片描述
自此大部分的函数接口都已经实现完毕
下面是完整代码:

完整代码

string.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cassert>namespace th
{const size_t EXPANSION_FACTOR = 2;const size_t INITIAL_CAPACITY = 8;class string{public:string(const char* str = "");string(const string&);string& operator=(string&);char* begin() const;char* end() const;char* begin();char* end();char& operator[](int index);~string();void insert(int index, char ch);void reserve(size_t n);void append(const char* str);void append(const char ch);string& operator+=(const char* str);string& operator+=(const char ch);void push_back(char ch);void erase(size_t pos, size_t len);private:size_t _size;size_t _capacity;char* _str;};std::ostream& operator<<(std::ostream& out, const th::string& str);
}

string.cpp

#include "string.h"th::string::string(const char* str): _size(0), _capacity(0), _str(nullptr)
{_size = strlen(str);_capacity = _size+1;_str = new char[_capacity];strcpy(_str, str);
}th::string::string(const string& str)
{string temp(str._str);std::swap(_str, temp._str);std::swap(_size, temp._size);std::swap(_capacity, temp._capacity);
}th::string& th::string::operator=(string& str)
{_str = str._str;_size = str._size;;_capacity = str._capacity;return *this;
}th::string::~string()
{delete[] _str;_str = nullptr;_size = 0;_capacity = 0;
}char* th::string::begin() const
{return _str;
}
char* th::string::end() const
{return _str + _size;
}char* th::string::begin() 
{return _str;
}
char* th::string::end()
{return _str + _size;
}std::ostream& th::operator<<(std::ostream& out, const th::string& str)
{for (auto e : str){out << e;}return out;}char& th::string::operator[](int index)
{assert(index >= 0 && index <= _size);return *(_str + index);
}void th::string::insert(int index, char ch)
{assert(index >= 0 && index <= _size);if (_size+1 == _capacity){size_t newCapacity = _capacity == 0 ? INITIAL_CAPACITY : _capacity * EXPANSION_FACTOR;reserve(newCapacity);}for (int i = _size-1; i >= index; i--){_str[i+1] = _str[i];}_str[index] = ch;_size++;
}void th::string::reserve(size_t n)
{if (n > _capacity){_capacity = n;char* tmp = new char[_capacity];strcpy(tmp, _str);delete[] _str;_str = tmp;}
}void th::string::append(const char* str)
{if (_size + 1 + strlen(str) >= _capacity){size_t newCapacity = (_size + 1 + strlen(str)) * EXPANSION_FACTOR;reserve(newCapacity);}strcpy(_str + _size, str);_size += strlen(str);
}th::string& th::string::operator+=(const char* str)
{append(str);return *this;
}void th::string::push_back(char ch)
{if (_size + 2 >= _capacity){size_t newCapacity = (_size + 2) * EXPANSION_FACTOR;reserve(newCapacity);}_str[_size] = ch;_str[_size + 1] = '\0';_size++;
}void th::string::append(const char ch)
{push_back(ch);
}th::string& th::string::operator+=(const char ch)
{push_back(ch);return *this;
}void th::string::erase(size_t pos, size_t len)
{assert(pos < _size && pos >= 0);assert(len >= 0);if (pos + len >= _size){_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}
}

         新人创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看。

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

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

相关文章

前端CSS3 渐变详解

文章目录 CSS3 渐变详解一、引言二、CSS3 渐变基础1、线性渐变1.1、基本线性渐变1.2、改变渐变方向 2、径向渐变2.1、基本径向渐变2.2、设置径向渐变的中心 三、高级渐变技巧1、重复渐变1.1、重复线性渐变1.2、重复径向渐变 四、总结 CSS3 渐变详解 一、引言 在现代网页设计中…

Ubuntu系统安装minicom软件连接交换机

安装minicom&#xff1a; 电脑主机串口线连接上交换机的console口。开打乌班图系统终端&#xff0c;输入sudo -i切换为root用户 方法一&#xff1a; 输入 sudo apt-get install minicom 命令&#xff0c;安装minicom软件。 minicom 必须带有安装包的完整路径 文件名称 后…

异星工厂_1

经验 首次体验异星工厂这款游戏&#xff0c;得出了以下经验。 1. 基地的构建顺序&#xff1a;煤&#xff0c;电&#xff0c;原料&#xff0c;传送流&#xff0c;组装器&#xff0c;防御武器&#xff0c;其他 2. 永远不要让采集&#xff08;生产者&#xff09;停止&#xff0…

前端面试题每日一学_6

今日一题&#xff1a; 下面的CSS代码中&#xff0c;定义了一个帧动画&#xff0c;请问该帧动画能否正常流畅的执行&#xff1f; keyframes move {50% {/* 改变自定义变量的值 */--x: 100px;/* 改变元素的背景颜色 并使用!important */background: yellow !important;} } .d {…

QScrollArea应用范例,包含完整代码

QScrollArea使用案例,如果只是拖控件,做简单的布局那应该不难,但如果在复杂的布局中,用纯代码的方式来应用QScrollArea还是有点繁琐的,下面我写一段话,可能会有点绕 你需要先将widget添加到scrollArea,然后再将scrollArea添加到它要去的layout然后再设置scrollArea里的w…

【操作系统】每日 3 题(十四)

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?typeblog &#x1f4e3;专栏地址&#xff1a;https://blog.csdn.net/newin2020/category_12820365.html &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享操作系统面试中常见的面试题给大家~ ❤️…

销售管理SCRM助力企业高效提升业绩与客户关系管理

内容概要 在当今这个快速变化的市场环境中&#xff0c;企业面临着日益加剧的竞争&#xff0c;寻找更高效的销售管理工具显得尤为重要。销售管理SCRM不仅是一个单纯的客户关系管理工具&#xff0c;更是推动企业业绩提升的重要助力。在这一背景下&#xff0c;SCRM以其独特的优势…

JAVA基础-多线程线程池

文章目录 1. 多线程1.1什么是多线程&#xff08;1&#xff09;并发和并行&#xff08;2&#xff09;进程和线程 1.2多线程的实现方式1.2.1 方式一&#xff1a;继承Thread类1.2.2 方式二&#xff1a;实现Runnable接口1.2.3方式三: 实现Callable接口 1.3 常见的成员方法1.3.1 设置…

【日记】清仓了三个基金(1199 字)

正文 今天好忙。而且是瞎忙。主要是办公室找的家具厂不靠谱&#xff0c;上次桌子尺寸量错了&#xff0c;得换。今天拿了新的来&#xff0c;又要腾一遍东西。上午时间就没了。 那个疑似洗钱的客户又来了。他开户意愿真的好强烈。没办法&#xff0c;上午把账号给他开了出来&#…

微服务的注册中心Nacos

前言 Nacos是阿里巴巴开源的服务注册中心以及配置中心&#xff0c;致力于给开发者提供一款便捷、简单上手的开源框架。 Nacos究竟有什么惊人的地方呢&#xff1f;看下图&#xff1a; 从上图不难看出阿里巴巴的野心&#xff0c;一个Nacos干掉了Spring Cloud的三大组件&#xf…

怎么启动python脚本文件

创建一个简单的python入门代码&#xff0c;以便示范。 存储文件并复制该python文件的存储路径。 使用cd 命令切换工作目录到python文件所在的目录。 输入变量环境中的python路径和python文件的名字。 回车执行后&#xff0c;可完成命令行的python文件运行。

vue2-vuex详解

目录 vuex构建vuex[多组件数据共享]环境创建一个空仓库核心概念 - state状态核心概念 - mutations方法mutations语法 核心概念 - actions处理异步操作核心概念 - getters核心概念 - 模块module(进阶语法) vuex 是什么 vuex是一个状态管理工具&#xff0c;状态就是数据 vuex是一…

苍穹外卖-day03

公共字段自动填充新增菜品菜品分页查询删除菜品修改菜品 功能实现&#xff1a; 菜品管理 1. 公共字段自动填充 1.1 问题分析 在上一章节我们已经完成了后台系统的员工管理功能和菜品分类功能的开发&#xff0c;在新增员工或者新增菜品分类时需要设置创建时间、创建人、修改…

最简单解决NET程序员在centos系统安装c#网站

目前随着技术栈转移&#xff0c;c#程序员如何在linux服务器中部署net程序呢&#xff1f; 我做了一次实验&#xff1a;一般来说runtime和sdk都要装。 1.centos系统内命令行输入命令 sudo yum install dotnet-sdk-6.0 安装6.0版 2.检测下是否成功&#xff1a;dotnet --versio…

【HarmonyOS——MVVM模式 | 理解MVVM模式,看这一篇就够了】

大家好&#xff0c;我是学徒小z&#xff0c;近期项目开发中遇到一些数据源放置混乱的问题&#xff0c;所以带来一篇MVVM模式的文章 文章目录 MVVM模式为什么要用MVVM模式对于鸿蒙中MVVM模式的疑惑ArkUI的MVVM项目结构中的MVVM1. 概述2 .分层说明3. 架构核心原则不可跨层访问下…

网络基础:http协议和内外网划分

声明 学习视频来自B站UP主泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 泷羽sec的个人空间-泷羽sec个人主页-哔哩哔哩视频https://space.bilibili.com/350329294 一&#xff0c;H…

【2024软考架构案例题】你知道 Es 的几种分词器吗?Standard、Simple、WhiteSpace、Keyword 四种分词器你知道吗?

👉博主介绍: 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO 专家博主 ⛪️ 个人社区:个人社区 💞 个人主页:个人主页 🙉 专栏地址: ✅ Java 中级 🙉八股文专题:剑指大厂,手撕 J…

CS61b part6

8.6 Implementation Inheritance and Default Method 让我们谈谈另一种类型的继承&#xff0c;这种继承与之前的关系紧密但精神上却非常不同&#xff0c;这种新的继承类型称为实现继承。我们之前看到的是接口继承&#xff0c;在这种方法中&#xff0c;子类获得了方法的签名&am…

C++——异常

异常是在程序执行的过程中发生了某种错误&#xff0c;异常的处理机制允许我们讲发生的异常抛出给程序的另外一部分&#xff0c;对这个错误进行处理。这个机制让问题检测的环节和问题处理的环节分离。检测环节只需要负责检测即可&#xff0c;无需关系解决的细节问题。在C语言中处…