模拟实现string
创始人
2024-04-08 17:16:49

image-20221116120149781

第一部分:构造,析构,拷贝构造,赋值重载,打印函数这几个大头写出来先

string类框架


namespace xxx
{
class string
{
public:
//
//

private:
char* _str;
size_t _size;
size_t _capacity;const static size_t npos = -1;//c++允许const static整数可以定义在类里-但只有几个特定的整数可以这样
};
}

构造函数

	//构造函数string(const char* str = "")//给缺省值//  :_size(strlen(str))//	,_capacity(strlen(str))//这里不用初始化列表,这里要一个个strlen赋值比较麻烦{_size = strlen(str);//_size为有效字符的长度_capacity = _size;//capacity初始化都为有效字符的长度_str = new char[_capacity + 1];//留一个字符给'\0'strcpy(_str, str);//拷贝过去}

析构函数

	//析构函数~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}

拷贝构造

		//拷贝构造-深拷贝噢string(const string& s)//传引用{_str = new char[s._capacity + 1];//留一个字符给斜杠0strcpy(_str, s._str);//_str拷贝_size = s._capacity;_capacity = s._capacity;}

赋值重载

	//赋值重载string& operator=(const string& s)//传引用{if (this != &s){char* tmp = new char[s._capacity + 1];//开新空间strcpy(tmp, s._str);//拷贝delete[]_str;//删除_str的就空间_str = tmp;//指向新空间_size = s._size;_capacity = s._capacity;}return *this;}

对于**【为什么要开一块新空间然后还要把之前的旧空间释放掉**】有疑惑的朋友可以来看这篇https://blog.csdn.net/m0_71841506/article/details/127291467

通过c_str打印

	//返回一个指向正规C字符串的指针常量, 内容与本string串相同const char* c_str()const{return 	_str;}

size和capacity

	size_t size()const{return _size;}// capacitysize_t capacity()const{return _capacity;}

基本上写到这就能输出hello world拉!

image-20221114203417918

第二部分:各类功能和升级

迭代器iterator

	typedef char* iterator;iterator begin(){return _str;//返回第一个指针位置}iterator end(){return _str + _size;//返回最后一个有效字符的下一个位置}

实现iterator之后我们可以这样玩

image-20221114204504544

还能用范围for

image-20221114204913418

但是我们把iterator实现的部分注释掉的话,范围for就用不了了;又或者说把iterator begin()改为iterator Begin()我们会发现iterator打印还能用,但范围for也用不了了;这说明范围for底层与iterator实现有关!至于为啥嘛。。。知道的可以在评论区留言~

方括号重载

char &operator[](int pos)const//方括号重载{return _str[pos];}

重载之后我们就能用数组【】访问

image-20221114214055011

push_back追加字符

		void push_back(char ch)//尾插字符{if (_size == _capacity)//扩容{size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;//在末尾追加字符ch_size++;_str[_size] = '\0';//加上\0}

operator+=追加字符运算符重载

	string& operator+=(char ch)//追加字符重载{push_back(ch);return *this;}

append追加字符串

	//后面追加追加字符串void append(const char* str){size_t len = strlen(str);//记录str有效字符串长度if (_size + len > _capacity)//直接扩容{reserve(_size + len);}strcpy(_str + _size, str);//strcpy把str连同\0一起拷贝过来了,所以不需要考虑\0_size += len;	}

operator+=追加字符串运算符重载

	string& operator+=(const char* str)//追加字符串重载{append(str);return *this;}

clear清除字符串内有效字符

	void clear()//清除所有有效字符{_str[_size] = '\0';_size = 0;}

流插入<<

	ostream& operator<<(ostream& out, const string&s )//流插入{for (size_t i = 0; i < s.size(); ++i){out << s[i];}return out;}

流提取>>

流提取是当遇到空格或者换行则截断提取,那么我们要按照这个底层来实现它

istream& operator>>(istream& in, string& s)//流提取{s.clear();//如果流提取之前s里面有字符,则要清除char buff[128] = { '\0' };//开一块空间做中转站size_t i = 0;char ch= in.get();//用ch一个个接收s的字符while (ch != ' ' && ch != '\n')//当ch接收到换行或者空格则停止{if (i == 127){//满了-buff尾接给ss += buff;i = 0;}//没满ch继续接收buff[i++] = ch;ch = in.get();}if (i>0)//如果没满则buff没有尾插到s后面;又或者buff满过的部分尾接给了s,但剩下的部分没有尾接给s{buff[i] = '\0';//要携带\0 !s += buff;
}return in;}

那么我们就能用流插入打印,流提取我们输入的内容拉

image-20221114223428759

image-20221115091827032

insert

size_t和int类型同时在操作符两边,会造成整形提升-小的往大的提升(int->size_t);那么int也转变为无符号数,则没有负数,比较大小会出错,会进入死循环;

insert插入字符

在pos位置插入字符ch

string& insert(size_t pos, char ch)//插入字符-{assert(pos <=_size);if (_size == _capacity)//扩容{size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}//挪动数据//size_t end = _size+1;//(size_t)无符号版//while (end>pos)//{//	_str[end] = _str[end-1];//	end--;//}//(int)有符号版int end = _size-1;while (end >=(int) pos){_str[end + 1] = _str[end];end--;}//插入字符_str[pos] = ch;_size++;_str[_size] = '\0';return *this;}

image-20221115101414713

insert插入字符串

在pos位置插入len长的字符串

string& insert(size_t pos, const char* str)//插入字符串{//扩容size_t len = strlen(str);//记录要插入字符串的长度if (_size + len > _capacity){reserve(_size + len);}//挪动数据//(size_t)无符号版//size_t end = _size+1;//+1把\0也挪到_str[_szie+len-1]的位置上//while (end > pos)//{//	_str[end + len-1] = _str[end-1];//	end--;//}//有(int)符号版int end = _size;//把\0也挪到_str[_size+len]位置上while (end >= (int)pos){_str[end + len] = _str[end];--end;}//插入字符串strncpy(_str + pos, str, len);_size += len;return *this;}

image-20221115104633841

erase 删除

从pos位置往后删除len个字符,不给len则按缺省值npos删除(全删完)

string& erase(size_t pos, size_t len = npos){assert(pos < _size);if (len == npos || len >= _size - pos)//如果len长度大于pos之后的有效字符串长度,那么也在pos位置\0{_str[pos] = '\0';_size = pos;}else{strcpy(_str + pos, _str + pos + len);_size -= len;}return *this;}

image-20221115114423146

find

find寻找字符

	size_t find(char ch, size_t pos=0)//寻找字符-给缺省值从0开始找{assert(pos < _size);while (pos<_size){if (_str[pos] == ch){return pos;//,返回下标}++pos;}return npos;//没找到返回npos}

image-20221115120002408

find寻找字符串

	size_t find(const char* s, size_t pos = 0)//寻找字符串-给缺省值从0开始找{assert(pos < _size);const char* ptr = strstr(_str, s);//strstr在字符串里面寻找字串-找到返回指针-没找到返回空if (ptr == nullptr){return npos;//没找到}else{return ptr - _str;//指针相减得指针之间字符串数量-即为pos的位置}}

image-20221115120051552

高级版本的拷贝构造和赋值重载

拷贝构造

我们要用s1来拷贝构造s2,那么我们可以先用s1来构造tmp;然后在让指向tmp的指针和指向s2的指针交换指向;那么s2也完成了对s1内容的深拷贝;(这里要注意:要给原本是野指针的s2一个初始化空指针,交换后保证后续tmp析构时不越界析构其他空间【析构函数遇到空指针不析构】

image-20221116111951347

image-20221116112611005

	//拷贝构造-现代写法//s2(s1)string(const string& s):_str(nullptr)//要初始化给个空指针,不然是野指针,tmp析构的时候会越界空间析构报错,_size(0),_capacity(0){string tmp(s._str);//用s构造tmpswap(_str,tmp._str);swap(_size, tmp._size);swap(_capacity, tmp._capacity);}

image-20221116112757177

但是我们盯着上面的现代写法,一共是交换了_str, _szie, _capacity一共三次深拷贝。那么能不能再简化呢?

我们通过查表格知道std库里的swap是先把对象实例化再交换;那么在这里是要进行三次深拷贝,深拷贝要开空间然后赋值。。。可见这样做代价非常大!

image-20221116113352782

string库里的swap是直接把两个string的指针交换这样代价相对小拉!

image-20221116113609167

所以我们可以写一个swap函数

string里的swap

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

那么简化后的版本是这样的:

//拷贝构造-现代写法-简化后//s2(s1)string(const string& s):_str(nullptr)//要初始化给个空指针,不然是野指针,tmp析构的时候会越界空间析构报错,_size(0),_capacity(0){string tmp(s._str);//用s构造tmp//this->swap(tmp);this可以不写swap(tmp);}

赋值重载

//赋值重载-现代写法string& operator=( const string& s)//传引用{if (this != &s){string tmp(s);//s构造tmpswap(tmp);//交换this和tmp指针}return *this;}

简化后版本

string& operator=(string& s)//传引用{if (this != &s){swap(s);//不需要tmp做中间人直接交换}return *this;}

关于string类的模拟实现的总结就到这里拉,看到这里的观众老爷们不妨点赞收藏起来看吧~~~

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...