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

C++中string的简单实现

string的简单实现中一些函数的实现可以复用一些其他的函数来实现;比较重要的是在实现是深浅拷贝问题

目录

string的结构

实现构造和析构 

reserve扩容

容量 

push_back和append

insert和erase的实现

swap的实现(不是成员函数但是string类的友元)

赋值运算符的重载和+=运算符的重载 

c_str和substr

关系运算符的重载

流插入和流提取运算符的重载(跟swap一样是string的友元)


string的结构

    class string{	private:typedef char* iterator;//普通迭代器typedef const char* const_iterator;//const迭代器char* _str;size_t _size;//长度,大小size_t _capacity;//容量static const size_t npos = -1;//npos相当于一个很大的数};

为啥用char* 指针就可以充当迭代器?

因为可以直接通过char*来访问string中的字符数组。在库中的实现可能不是用的char*。



实现构造和析构 

		string(const char* s = "");//构造 默认参数即使不传参也可以构造一个空字符串string(const string& s);//拷贝构造~string();//析构

 为什么需要显示实现析构函数?

因为_str指向的空间是new出来的,是资源,所以需要用delete手动释放该资源。

	string::string(const char* s):_size(strlen(s)){_str = new char[_size + 1];strcpy(_str, s);_capacity = _size + 1;}string::string(const string& s){_str = new char[s._capacity + 1];strcpy(_str, s._str);_size = s._size;_capacity = s._capacity;}string::~string(){delete[] _str;_size = _capacity = 0;}

实现没有什么就是简单的对字符串的操作。


reserve扩容

	void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n];strcpy(tmp, _str);delete[] _str;//释放原来的空间_str = tmp;_capacity = n;}}

注意一定要释放原来的空间否则会内存泄漏 

 操作简图


容量 

	size_t string::size(){return _size;}size_t string::capacity(){return _capacity;}size_t string::size()const//const修饰的string会调用该函数{return _size;}size_t string::capacity()const{return _capacity;}

 


push_back和append

	void string::push_back(char ch){//扩容if (_size == _capacity){int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size++] = ch;//别忘了在最后加上‘\0’_str[_size] = '\0';}string& string::append(const char* s){int len = strlen(s);//扩容if (_size + len >= _capacity){reserve(_size + len+1);//需要多扩一个字节来存放‘\0’}strcpy(_str + _size, s);_size = _size + len;return *this;//返回调用append这个函数的string对象本身}

注意:向string中插入新的数据,就有可能需要扩容,扩容正好可以复用之前实现的reserve函数。 


insert和erase的实现

	string& string::insert(size_t pos, const char* s){assert(pos >= 0 && pos < _size);//pos这个要插入的位置要合法int len = strlen(s);//扩容if (_size + len >= _capacity){reserve(_size + len +1);}int end = _size + len;//将pos位置之后的字符整体向后移动len(插入字符串的长度)个字节while (end - len >= pos){_str[end] = _str[end - len];end--;}//插入字符串for (int i = 0; i < len; i++){_str[pos + i] = s[i];}//strcpy(_str+pos,s);也可以直接这样写_size = _size + len;return *this;}string& string::insert(size_t pos,char ch){assert(pos >= 0 && pos < _size);if (_size + 1 >= _capacity)reserve(_size + 1 +1);int end = _size;//将pos位置之后的字符整体向后移动1个字节while (end >= pos){_str[end + 1] = _str[end];end--;}_str[pos] = ch;_size++;return *this;}
	string& string::erase(size_t pos, size_t len){//如果长度很长就直接将pos位置设置为‘\0’if (pos + len > _size){_str[pos] = '\0';_size = pos;}else{//如果长度不是很长,那么就pos+len位置之后的字符整体前移for (size_t i = pos; i+len <= _size; i++){_str[i] = _str[i + len];}_size-=len;}return *this;}

 

swap的实现(不是成员函数但是string类的友元)

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

 

 在标准库中有实现的一个swap的函数模板

//标准库中的swap的实现
template<class T>
void swap(T& a, T& b)
{T tmp = a;a = b;b = tmp;
}

如果使用std::swap来交换两个字符串效率不高:tmp需要调用拷贝构造,重新拷贝一份a;将b赋值给a和将tmp赋值给b都会调用赋值运算符,重新开辟空间,释放掉原来的空间。总结,需要完成三次深拷贝,性能不高。

 而为string专门实现的swap就只需要交换资源就可以。效率很高。


赋值运算符的重载和+=运算符的重载 

 

	string& string::operator=(const string& s){if (this != &s)//如果自己赋值给自己那么就什么也不需要做{char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}

赋值重载的另一个版本 

	string& string::operator=(string s){swap(*this,s);//直接调用swap交换两个*this和s两个资源就可以了return *this;}

直接将临时变量的资源与*this对象的资源互换,并且临时变量在销毁时也会delete释放资源。

跟上面的代码效率差不多,但如果是自己赋值自己,那么下面就会进行一次深拷贝,而上面啥也不做,效率不如上面。 


c_str和substr

	//返回字符串首元素地址const char* string::c_str(){return _str;}//返回字符串中的一段字符构造的stringstring string::substr(size_t pos, size_t len){string tmp;//len的长度很长直接将pos后的所有字符用来构造stringif (pos + len >= _size){for (int i = pos; i <= _size; i++){tmp += _str[i];}return tmp;}int i;for (i = 0; i < len; i++){tmp += _str[pos+i];}tmp += '\0';return tmp;}

这里的substr的实现直接复用了+=运算符


关系运算符的重载

	bool string::operator==(string& s){return strcmp(_str, s._str)==0;}bool string::operator!=(string& s){return !(*this == s);}bool string::operator>(string& s){return strcmp(_str, s._str) > 0;}bool string::operator>=(string& s){return (*this == s) || (*this > s);}bool string::operator<(string& s){return !(*this >= s);}bool string::operator<=(string& s){return (*this < s) || (*this == s);}

有的运算符的重载可以复用已经实现的运算符

流插入和流提取运算符的重载(跟swap一样是string的友元)

	istream& operator>>(istream& in,string& s){s.clear();char ch = in.get();//get从流(string)中读入一个charwhile (ch != ' ' && ch != '\n'){s += ch;ch = in.get();}return in;}ostream& operator<<(ostream& out,const string& s){for (int i = 0; i <s._size; i++){out << s[i];}return out;}

完整的string实现在gitee中: 

刷题记录: 用来调试刷题是写的代码 (gitee.com)

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • R语言xlsx,txt文件处理:以《书摘》00年-10年资源合集整理为例
  • 微信小程序npm扩展能力探究
  • CCS10导入CCS3.3工程
  • 287. 寻找重复数(stl法)
  • 【机器学习】和【人工智能】在量子力学的应用及代码案例分析
  • Spring Cloud 八股文
  • 【笔记】物理化学绪论
  • 【video clips 专栏 2 -- videopad 视频拼接】
  • 【LLM】局域网内为容器服务启用HTTPS
  • Spring中Bean的相关注解
  • ROM修改进阶教程------如何修改固件 线刷转卡刷 卡刷转线刷 操作中的一些注意事项
  • C++20中头文件bit的使用
  • k8s的环境配置
  • 【Linux】【Vim】Vim 基础
  • Python 数据分析— Pandas 基本操作(下)
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • ComponentOne 2017 V2版本正式发布
  • es6
  • mongodb--安装和初步使用教程
  • PAT A1120
  • Python - 闭包Closure
  • python大佬养成计划----difflib模块
  • Solarized Scheme
  • tab.js分享及浏览器兼容性问题汇总
  • ubuntu 下nginx安装 并支持https协议
  • Vue组件定义
  • Web标准制定过程
  • 闭包--闭包作用之保存(一)
  • 关于for循环的简单归纳
  • 前嗅ForeSpider中数据浏览界面介绍
  • 如何使用Mybatis第三方插件--PageHelper实现分页操作
  • 为物联网而生:高性能时间序列数据库HiTSDB商业化首发!
  • 系统认识JavaScript正则表达式
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​经​纬​恒​润​二​面​​三​七​互​娱​一​面​​元​象​二​面​
  • ​人工智能书单(数学基础篇)
  • ​如何使用QGIS制作三维建筑
  • #nginx配置案例
  • #前后端分离# 头条发布系统
  • $jQuery 重写Alert样式方法
  • (javaweb)Http协议
  • (二)pulsar安装在独立的docker中,python测试
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (汇总)os模块以及shutil模块对文件的操作
  • (三)c52学习之旅-点亮LED灯
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (未解决)macOS matplotlib 中文是方框
  • (转)ABI是什么
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .CSS-hover 的解释