【c++】string的模拟实现

目录

 一.  交换函数swap

二.  默认成员函数 

     构造函数和析构函数

     拷贝构造函数和赋值运算符重载

三.  容量相关操作接口

     size 与 capacity

     reserve 与 resize

附:reserve与resize的区别

四.  修改相关操作接口

    push_pack 

    append   

    insert 与 erase   

    operator+=

    find 

   substr

   clear

五.  遍历访问相关接口

   使用迭代器

  operator[ ] 

 六.  非成员函数流插入和流提取运算符重载 (<<、>>)

附:完整代码实现


示:string相关的操作接口有很多,这里实现了部分常用接口


                          

   

 一.  交换函数swap

           在实现string之前先提供以下三个交换函数,如下:

   

//用于交换的交换函数模板
template <class T> 
void swap(T& a, T& b)
{T tmp = a;a = b; b = tmp;
}

    这是一个全局的交换函数,是一个泛型版本。此交换函数被定义在库当中,即头文件为<algorithm>的标准算法库里但是,如果是交换一般的数据还是可以的;若是想要交换一个存储大量数据的类内容时,因这个交换函数涉及一个复制构造和两个赋值操作,此函数并不是最有效方法。

此时有另外两个交换函数:

  这个交换函数是string当中的,是一个非成员函数;其实这是上面通用算法交换的重载,它通过相互转移对其内部数据的所有权来提高其性能即,字符串交换对其数据的引用,而不实际复制字符


void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);
}

    该交换函数使用行为类似于此成员函数的优化重载 ; 这是为处理大型数据类型提供此函数的重载版本,优化其性能,即只交换几个内部指针而不是它们的全部内容,从而使它们在恒定时间内运行。

说明:以下string的有关拷贝构造赋值操作将用到以上的交换函数。

二.  默认成员函数 

       构造函数和析构函数

构造函数:①写两个构造函数(无参和带参);②直接写一个带缺省参数的构造函数(本次采                        用这个)         

  带缺省参数的构造函数:   

    缺省参数置为空字符串

析构函数:

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

拷贝构造函数和赋值运算符重载

拷贝构造:一个已存在的对象去拷贝初始化另一个对象。

首先先拷贝构造一个临时对象,在复用上面开始时所说的交换函数,交换两者指针即可


string(const string& s)
{string tmp(s._str); //拷贝构造临时对象tmpswap(tmp);        //交换两者数据,只需要交换两者指针即可
}

赋值运算符重载:将两个已存在的对象进行拷贝——赋值重载

string& operator=(string s) //注意这里不要加引用&
{swap(s);return *this;
}

说明:参数中不加引用的目的就是不改变原对象的中的内容。若加了引用就会改变原对象的内容,也即将s1与s2直接进行交换;这样虽然将s1赋给了s2,但s1本身也改了,这就不符合我们的需求。

三.  容量相关操作接口

       size 与 capacity

 这是符合字符串内容的实际字节数,不一定等于其存储容量 ;返回字符串的有效数据长度(以字节为单位)。

     

size_t size() const
{return _size;
}

返回当前为字符串分配的存储容量的大小容量不一定等于字符串长度,它可以相等或更大。

size_t capacity() const
{return _capacity;
}

reserve 与 resize

reserve : 如果 n 大于当前字符串容量,则该函数会导致容器将其容量增加到 n 个字符(或更大)

此函数对字符串长度没有影响,并且无法更改其内容;但若 n 小于当前字符串容量,有些平台会缩容(linux),但是不会影像数据;有些不会(vs),取决于编译器,即总的来说,该函数会影响容量,但不会影响有效数据

void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1]; //开辟新的空间strcpy(tmp, _str); //拷贝原数据到新空间delete[] _str;   //释放原空间_str = tmp;_capacity = n; //更新容量}
}

resize:将字符串大小调整为 n 个字符的有效长度。
如果 n 小于当前字符串长度,则当前值将缩短为其前 n 字符,并删除第 n 个字符以外的字符。
如果 n 大于当前字符串长度,则通过在末尾插入任意数量的字符来扩展当前内容,以达到 n 的大小。

如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符(null字符)。

void string::resize(size_t n, char c)
{//n小于元素的个数if (n < _size){_size = n;_str[_size] = '\0'; //末尾处理\0}else {//n大于当前容量if (n > _capacity){reserve(n);    //扩容}for (size_t i = _size; i < n; i++)//将size个元素后的填充为字符c{_str[i] = c;}_str[n] = '\0';_size = n;}
}

附:reserve与resize的区别

reserve用于预留空间,但不会改变容器的大小,只是增加容器的容量,如果超过容器的容量,会重新分配内存

resize则是直接改变容器的大小,如果改变后的大小比原来的小,会删掉多余的元素,如果比原来的大,会添加默认值的元素。

总之:reserve 改变容器的容量——对应capacity

           resize 改变容器的大小——对应size,resize后 都为 有效元素

四.  修改相关操作接口

       push_pack 

 push_back:将字符 c 追加到字符串的末尾,使其长度增加 一。

void push_back(char c)
{if (_size == _capacity) //检查容量{size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = c;_size++;_str[_size] = '\0'; //注意处理末尾\0
}

 append

  

 功能:在字符串当前值的末尾追加一个字符串

string& append(const char* str)
{size_t len = strlen(str);    //计算追加字符串的长度if (_size + len > _capacity) //检查容量{reserve(_size + len);}strcpy(_str + _size, str);  //把要追加的字符串追加到该字符串的末尾_size += len;return *this;
}

   

insert 与 erase 

insert在pos位置插入一个字符

// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char c)
{assert(pos <= _size);if (_size == _capacity) //检查容量{size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}//注意size_t为无符号;这种写法程序会崩/*size_t end = _size;while (end >= pos)           {_str[end + 1] = _str[end];--end;}*/size_t end = _size + 1;  while (end > pos)         //pos位置后元素向后挪{_str[end] = _str[end - 1];--end;}_str[pos] = c;_size++;return *this;
}

           

insert在pos位置插入一个字符串

string& 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 + 1;while (end > pos)              //pos后的元素向后挪动(end+len)个长度{_str[end + len - 1] = _str[end - 1];--end;}strncpy(_str + pos, str, len);  //注意这里不要用strcpy_size += len;return *this;
}

说明:①挪动数据时从\0 开始挪动 (即 \0 也要挪动)

           ②这里不使用strcpy 而 使用strncpy,  是因为strcpy会把 \0 也拷贝进去,这样拷贝后有效数据元素的个数(即size)就不一定对了。

    erase 

功能:删除字符串值中从字符位置 pos 开始并跨越 len 个字符的部分(如果内容太短            或 len 为 nops,则删除直到字符串末尾的部分。

注意:如果不写len(即为默认参数),则会删除pos往后的所有字符(有多少删多少) 

// 删除pos位置上的元素,并返回该元素的下一个位置string& erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;return *this;}strcpy(_str + pos, _str + pos + len);_size -= len;return *this;}

          图解 :

     

    operator+=

  

   功能:在字符串当前值的末尾追加一个字符

string& operator+=(char c)
{push_back(c); //复用push_back即可return *this;
}

功能:在字符串当前值的末尾追加一个字符串

string& operator+=(const char* str)
{append(str);  //复用append即可return *this;
}

     find 

功能:从指定的pos位置处开始(若不指定默认从第一个字符开始)查找一个匹配的字符c

返回值:返回c在字符串中第一次出现的位置

// 返回c在string中第一次出现的位置
size_t find(char c, size_t pos) const
{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;
}

                      

功能:从指定的pos位置处开始(若不指定默认从第一个字符开始)查找一个匹配的字符串

返回值:第一个匹配项的第一个字符的位置。
              如果未找到匹配项,该函数将返回 string::npos

// 返回子串s在string中第一次出现的位置
size_t find(const char* s, size_t pos) const
{const char* ptr = strstr(_str + pos, s);if (ptr == nullptr)return npos;return ptr - _str;
}

     substr

 返回值:返回一个新构造的对象,其值初始化为此对象的子字符串的副本   

string substr(size_t pos, size_t len)
{assert(pos < _size);size_t end = len + pos;if (len == npos || pos + len >= _size){end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;
}

     图解:     

   clear

         功能:擦除字符串的内容,该字符串将变为空字符串(长度为 0 个字符)。

void clear()
{_str[0] = '\0'; //注意首字符置为\0_size = 0;
}

五.  遍历访问相关接口

       使用迭代器

 begin:

功能:如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器。         

//返回iterator迭代器 
iterator begin()
{return _str;
}//返回const_iterator迭代器
const_iterator begin() const
{return _str;
}

end:

功能:如果字符串对象是 const 限定的,则该函数返回const_iterator。否则,它将返回一个迭代器。

//返回iterator 迭代器
iterator end()
{return _str + _size;
}//返回const_iterator 迭代器
const_iterator end() const
{return _str + _size;
}

operator[ ] 

   

char& operator[](size_t pos)
{assert(pos <= _size);return _str[pos];
}const char& operator[](size_t pos) const
{assert(pos <= _size);return _str[pos];
}

 六.  非成员函数流插入和流提取运算符重载 (<<、>>)

  <<:       功能:将符合 str 值的字符序列插入到 os 中。  

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

>>:

功能:从输入流中提取字符串,将序列存储在 str 中,该序列被覆盖(替换了 str 的先前值)。

注意:输入流提取操作使用空格作为分隔符(在获取字符时会自动忽略空格),因此操作将仅从中提取可视为单词的内容

istream& operator>>(istream& in, string& s)
{char ch = in.get(); //这里用get()获取字符(包括空格),直到遇到换行结束while (ch != ' ' && ch != '\0'){s += ch;ch = in.get();}return in;
}

附:完整代码实现

#include <iostream>
#include <assert.h>
using namespace std;namespace mystring
{class string{public:typedef char* iterator;typedef const char* const_iterator;public:string(const char* str = "");~string();string(const string& s);string& operator=(const string& s);void resize(size_t n, char c);void resize(size_t n);void reserve(size_t n);void push_back(char c);string& operator+=(char c);string& operator+=(const char* str);void append(const char* str);void clear();void swap(string& s);char& operator[](size_t pos);const char& operator[](size_t pos) const;size_t find(char c, size_t pos = 0) const;       size_t find(const char* s, size_t pos = 0) const; string substr(size_t pos = 0, size_t len = npos);string& insert(size_t pos, char c);              string& insert(size_t pos, const char* str);string& erase(size_t pos, size_t len = npos);     private:char* _str;size_t _size;size_t _capacity;const static size_t npos = -1;};string::string(const char* str){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);}string(const string& s){string tmp(s._str);swap(tmp);}string& operator=(string s){swap(s);return *this;}string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// 容量size_t size() const{return _size;}size_t capacity() const{return _capacity;}void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}void string::resize(size_t n, char c){if (n < _size){_size = n;_str[_size] = '\0';}else {if (n > _capacity)reserve(n);for (size_t i = _size; i < n; i++){_str[i] = c;}_str[n] = '\0';_size = n;}}void string::resize(size_t n){if (n < _size){_size = n;_str[_size] = '\0';}else{if (n > _capacity)reserve(n);for (size_t i = _size; i < n; i++){_str[i] = '\0';}_size = n;}}// 修改void string::push_back(char c){if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}_str[_size] = c;_size++;_str[_size] = '\0';}string& string::operator+=(char c){push_back(c);return *this;}string& string::operator+=(const char* str){append(str);return *this;}void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len);}strcpy(_str + _size, str);_size += len;}void string::clear(){_str[0] = '\0';_size = 0;}void string::swap(string& s){std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capacity, s._capacity);}// 访问iterator begin(){return _str;}const_iterator begin() const{return _str;}iterator end(){return _str + _size;}const_iterator end() const{return _str + _size;}char& string::operator[](size_t pos){assert(pos <= _size);return _str[pos];}const char& string::operator[](size_t pos) const{assert(pos <= _size);return _str[pos];}// 返回c在string中第一次出现的位置size_t string::find(char c, size_t pos) const{for (size_t i = pos; i < _size; i++){if (_str[i] == c)return i;}return npos;}// 返回子串s在string中第一次出现的位置size_t string::find(const char* s, size_t pos) const{const char* ptr = strstr(_str + pos, s);if (ptr == nullptr)return npos;return ptr - _str;}string string::substr(size_t pos, size_t len){assert(pos < _size);size_t end = len + pos;if (len == npos || pos + len >= _size){end = _size;}string str;str.reserve(end - pos);for (size_t i = pos; i < end; i++){str += _str[i];}return str;}// 在pos位置上插入字符c/字符串str,并返回该字符的位置string& string::insert(size_t pos, char c){assert(pos <= _size);if (_size == _capacity){size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;reserve(newcapacity);}size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = c;_size++;return *this;}string& string::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 + 1;while (end > pos){_str[end + len - 1] = _str[end - 1];--end;}strncpy(_str + pos, str, len);_size += len;return *this;}// 删除pos位置上的元素,并返回该元素的下一个位置string& string::erase(size_t pos, size_t len){assert(pos < _size);if (len == npos || pos + len >= _size){_str[pos] = '\0';_size = pos;return *this;}strcpy(_str + pos, _str + pos + len);_size -= len;return *this;}//流插入、流提取ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){char ch = in.get();while (ch != ' ' && ch != '\0'){s += ch;ch = in.get();}return in;}}


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

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

相关文章

软件设计师——数据结构(一)

&#x1f4d1;前言 本文主要是【数据结构】——软件设计师——数据结构的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304…

时序分解 | Matlab实现DBO-VMD基于蜣螂优化算法优化VMD变分模态分解时间序列信号分解

时序分解 | Matlab实现DBO-VMD基于蜣螂优化算法优化VMD变分模态分解时间序列信号分解 目录 时序分解 | Matlab实现DBO-VMD基于蜣螂优化算法优化VMD变分模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.利用蜣螂优化算法优化VMD中的参数k、a&…

千亿露酒市场的未来之“露”

执笔 | 尼 奥 编辑 | 扬 灵 12月15日&#xff0c;以“以美为酿&#xff0c;品致未来”为主题的中国露酒产业发展大会暨露酒价值论坛在“中国酒都”宜宾举办。 近年来&#xff0c;露酒产业发展异军突起&#xff0c;市场销售规模超越黄酒、葡萄酒品类&#xff0c;成为中国酒…

人工智能文本分类

在本文中&#xff0c;我们全面探讨了文本分类技术的发展历程、基本原理、关键技术、深度学习的应用&#xff0c;以及从RNN到Transformer的技术演进。文章详细介绍了各种模型的原理和实战应用&#xff0c;旨在提供对文本分类技术深入理解的全面视角。 一、引言 文本分类作为人工…

在线客服系统定价因素解析:影响价格的关键因素

跨境电子商务公司必不可少的工具就是在线客服系统。企业选择在线客服系统的时候免不了要对不同产品的功能性、价格、服务等因素进行考量。今天这篇文章&#xff0c;我们就来探讨一下在线客服系统的定价因素有哪些&#xff1f;探究市面上的在线客服系统价格各异的影响因素。为大…

c# bitmap压缩导致png不透明的问题解决

新建.net 6控制台项目 安装System.Drawing.Common包 代码如下 using System.Drawing; using System.Drawing.Imaging;namespace PngCompress02 {internal class Program{static void Main(string[] args){CompressPngImage("E:\Desktop\6.png", "E:\Desktop\6…

C++相关闲碎记录(14)

1、数值算法 &#xff08;1&#xff09;运算后产生结果accumulate() #include "algostuff.hpp"using namespace std;int main() {vector<int> coll;INSERT_ELEMENTS(coll, 1, 9);PRINT_ELEMENTS(coll);cout << "sum: " << accumulate(…

Python - coverage

coverage overage 是一个用于测量Python程序代码覆盖率的工具。它监视您的程序&#xff0c;注意代码的哪些部分已经执行&#xff0c;然后分析源代码&#xff0c;以确定哪些代码本可以执行&#xff0c;但没有执行。 覆盖率测量通常用于衡量测试的有效性。它可以显示代码的哪些…

整理了上百个开源中文大语言模型,涵盖模型、应用、数据集、微调、部署、评测

自ChatGPT为代表的大语言模型&#xff08;Large Language Model, LLM&#xff09;出现以后&#xff0c;由于其惊人的类通用人工智能&#xff08;AGI&#xff09;的能力&#xff0c;掀起了新一轮自然语言处理领域的研究和应用的浪潮。 尤其是以ChatGLM、LLaMA等平民玩家都能跑起…

抖音品牌力不足,如何开通抖音旗舰店?强开旗舰店全攻略来了!

随着直播的兴起&#xff0c;抖音电商在近年来的发展速度可谓是相当迅猛。越来越多的商家开始将重心投入到抖音电商。从开店、搭建直播间&#xff0c;起号&#xff0c;再到日常运营... 然而我们在第一步开店的时候&#xff0c;就遇到了不少麻烦。 1、选择开通抖音旗舰店&#x…

Spring Cloud + Vue前后端分离-第5章 单表管理功能前后端开发

Spring Cloud Vue前后端分离-第5章 单表管理功能前后端开发 完成单表的增删改查 控台单表增删改查的前后端开发&#xff0c;重点学习前后端数据交互&#xff0c;vue ajax库axios的使用等 通用组件开发:分页、确认框、提示框、等待框等 常用的公共组件:确认框、提示框、等待…

系列九、事务

一、事务 1.1、概述 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或者撤销操作请求&#xff0c;即&#xff1a;这些操作要么同时成功&#xff0c;要么同时失败。 例如: 张三给李四转账1000块钱&…

使用邮件群发平台,轻松实现高效沟通的4大优势!

新媒体带动着众多线上平台的发展&#xff0c;使得流量为企业带来了可观的营收。但是&#xff0c;随着短视频市场的饱和&#xff0c;想要再次获得初始时的流量就变得越发困难。在这个时候&#xff0c;企业不妨将眼光往邮件群发这个传统的营销方式上倾斜&#xff0c;特别是出海、…

数据结构之---- 动态规划

数据结构之---- 动态规划 什么是动态规划&#xff1f; 动态规划是一个重要的算法范式&#xff0c;它将一个问题分解为一系列更小的子问题&#xff0c;并通过存储子问题的解来避免重复计算&#xff0c;从而大幅提升时间效率。 在本节中&#xff0c;我们从一个经典例题入手&am…

盛最多水的容器

给定一个长度为 n 的整数列表 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容器。 示例1&…

Python基础01-环境搭建与输入输出

零、文章目录 Python基础01-环境搭建与输入输出 1、Python概述 &#xff08;1&#xff09;为什么要学习Python 技术趋势&#xff1a;Python自带明星属性&#xff0c;热度稳居编程语言界前三 简单易学&#xff1a;开发代码少&#xff0c;精确表达需求逻辑&#xff1b;33个关…

什么是Maven?

什么是Maven 1、Maven是依赖管理、项目构建工具。 pom.xml springBoot项目的核心配置文件&#xff0c;pom项目对象模型、Dependency依赖管理模型。 Maven中的GAVP是指&#xff1a; 1、GroupId&#xff1a;当前工程组织id&#xff0c;例如&#xff1a;com.jd.tddl 2、ArtifactI…

IS-IS原理与配置

IS-IS原理与配置 • IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP &#xff08;ConnectionL…

[ 8 种有效方法] 如何在没有备份的情况下恢复 Android 上永久删除的照片?

我们生命中最重要的时刻&#xff0c;但这样做有缺点&#xff0c;其中之一就是数据丢失的风险。您可能倾向于定期删除无意义的照片&#xff0c;同时保存可爱的照片&#xff0c;从而使您的 Android 设备井井有条。然而&#xff0c;有些人在删除自己珍视的图像时不小心犯了错误。您…