【C++】string的模拟实现

【C++】string的模拟实现

文章目录

string的模拟实现

1.1 经典的string类问题

上面已经对string类进行了简单的介绍,大家只要能够正常使用即可。在面试中,面试官总喜欢让 学生自己来模拟实现string类,最主要是实现string类的构造、拷贝构造、赋值运算符重载以及析 构函数。大家看下以下string类的实现是否有问题?

// 为了和标准库区分,此处使用StringclassString{public:/*String() :_str(new char[1]) {*_str = '\0';} *///String(const char* str = "\0") 错误示范//String(const char* str = nullptr) 错误示范String(constchar* str =""){// 构造String类对象时,如果传递nullptr指针,可以认为程序非if(nullptr== str){assert(false);return;} _str =newchar[strlen(str)+1];strcpy(_str, str);}~String(){if(_str){delete[] _str; _str =nullptr;}}private:char* _str;};// 测试voidTestString(){ String s1("hello bit!!!"); String s2(s1);}
在这里插入图片描述

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认 的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1、s2共用同一块内 存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

1.2 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致 多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该 资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。

就像一个家庭中有两个孩子,但父母只买了一份玩具,两个孩子愿意一块玩,则万事大吉,万一 不想分享就你争我夺,玩具损坏。

可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。父 母给每个孩子都买一份玩具,各自玩各自的就不会有问题了。

在这里插入图片描述

1.3 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给
出。一般情况都是按照深拷贝方式提供。

在这里插入图片描述

模拟实现

构造函数的模拟实现

//传统写法/*string(const string& s) { _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; }*///s2(s1) tmp是s2//现代写法string(const string& s){ string tmp(s._str);//默认构造swap(tmp);//把tmp和s2交换}

赋值重载的模拟实现

//s2=s1 赋值//传统写法/*string& operator=(const string& s) { if (this != &s) { delete[] _str; _str = new char[s._capacity + 1]; _size = s._size; _capacity = s._capacity; } return *this; }*///s1=s3//string& operator=(const string& s)//{// if (this != &s)// {// //把s3的内容都给tmp.然后s1与tmp交换// //string tmp(s._str); 调用默认构造// string tmp(s);//调用拷贝构造// swap(tmp);// }// //而且tmp出了作用域自动销毁// return *this;//}//最新版本的赋值 string&operator=(string tmp)//拷贝构造必须用引用,不然会无穷递归 但赋值不用{swap(tmp);return*this;}

析构的模拟实现

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

clear的模拟实现

voidclear(){ _str[0]='\0'; _size =0;}

reserve的模拟实现

void string::reserve(size_t n){if(n > _capacity){char* tmp =newchar[n +1];//永远多开一个空间strcpy(tmp, _str);delete[] _str; _str = tmp; _capacity = n;}}

push_back的模拟实现

void string::push_back(char ch){if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} _str[_size]= ch;++_size; _str[_size]='\0';}

append的模拟实现

void string::append(constchar* str){ size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);}strcpy(_str + _size, str);//_str+size就是\0的位置 _size += len;}

insert的模拟实现

void string::insert(size_t pos,char ch)//插字符{assert(pos <= _size);if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} size_t end = _size+1;while(end > pos){ _str[end]= _str[end-1];//如果end=_size _str[end+1]=_str[size] 当end减到-1时与pos比较会隐式类型转换--end;} _str[pos]= ch;++_size;}void string::insert(size_t pos,constchar* str)//插字符串{assert(pos <= _size);//扩容 size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);} size_t end = _size + len;if(len ==0)return;while(end > pos + len-1)//end=pos+len时也要继续{ _str[end]= _str[end-len];--end;}for(size_t i =0; i < len;++i){ _str[pos+i]= str[i];} _size += len;}

erase的模拟实现

void string::erase(size_t pos, size_t len )//缺省参数在声明的时候给{assert(pos < _size);if(len > _size - pos){ _str[pos]='\0'; _size =pos;}else{for(size_t i = pos + len;i<=_size ; i++){ _str[i - len]= _str[i];} _size -= len;}}

find的模拟实现

size_t string::find(char ch, size_t pos){for(size_t i = pos; i < _size; i++){if(_str[i]== ch)return i;}return npos;} size_t string::find(constchar* str, size_t pos){assert(pos < _size);constchar* ptr =strstr(_str + pos, str);//strstr返回第一个字串位置if(ptr ==nullptr){return npos;}else{return ptr - _str;}}

substr的模拟实现

string string::substr(size_t pos , size_t len ){assert(pos < _size);//len太大就调整一下if(len > _size - pos){ len = _size - pos;} string sub; sub.reserve(len);for(size_t i=0;i<len;++i){ sub+= _str[pos + i];}return sub;}

比较运算符的模拟实现

booloperator<(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())<0;}booloperator==(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())==0;}booloperator>(const string& s1,const string& s2){return!(s1 < s2 || s1 == s2);}booloperator<=(const string& s1,const string& s2){return s1 < s2 || s1 == s2;}booloperator>=(const string& s1,const string& s2){return!(s1 < s2);}booloperator!=(const string& s1,const string& s2){return!(s1 == s2);}

流插入的模拟实现

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

流提取的模拟实现

istream&operator>>(istream& in, string& s){ s.clear();constint N =256;char buff[N];int i =0;char ch;//in >> ch; cin默认提取不到空格跟换行 ch = in.get();while(ch !=' '&& ch !='\n'){ buff[i++]= ch;if(i == N -1){ buff[i]='\0'; s += buff; i =0;} s += ch;//in >> ch; ch = in.get();if(i >0){ buff[i]='\0'; s += buff;}}return in;}

完整代码

string.cpp

#include"string.h"namespace bit {void string::reserve(size_t n){if(n > _capacity){char* tmp =newchar[n +1];//永远多开一个空间strcpy(tmp, _str);delete[] _str; _str = tmp; _capacity = n;}}void string::push_back(char ch){if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} _str[_size]= ch;++_size; _str[_size]='\0';} string& string::operator+=(char ch){push_back(ch);return*this;}void string::append(constchar* str){ size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);}strcpy(_str + _size, str);//_str+size就是\0的位置 _size += len;} string& string::operator+=(constchar* str){append(str);return*this;}void string::insert(size_t pos,char ch){assert(pos <= _size);if(_size == _capacity){reserve(_capacity ==0?4: _capacity *2);} size_t end = _size+1;while(end > pos){ _str[end]= _str[end-1];//如果end=_size _str[end+1]=_str[size] 当end减到-1时与pos比较会隐式类型转换--end;} _str[pos]= ch;++_size;}void string::insert(size_t pos,constchar* str){assert(pos <= _size);//扩容 size_t len =strlen(str);if(_size + len > _capacity){reserve(_size + len > _capacity *2? _size + len : _capacity *2);} size_t end = _size + len;if(len ==0)return;while(end > pos + len-1)//end=pos+len时也要继续{ _str[end]= _str[end-len];--end;}for(size_t i =0; i < len;++i){ _str[pos+i]= str[i];} _size += len;}void string::erase(size_t pos, size_t len )//缺省参数在声明的时候给{assert(pos < _size);if(len > _size - pos){ _str[pos]='\0'; _size =pos;}else{for(size_t i = pos + len;i<=_size ; i++){ _str[i - len]= _str[i];} _size -= len;}} size_t string::find(char ch, size_t pos){for(size_t i = pos; i < _size; i++){if(_str[i]== ch)return i;}return npos;} size_t string::find(constchar* str, size_t pos){assert(pos < _size);constchar* ptr =strstr(_str + pos, str);//strstr返回第一个字串位置if(ptr ==nullptr){return npos;}else{return ptr - _str;}} string string::substr(size_t pos , size_t len ){assert(pos < _size);//len太大就调整一下if(len > _size - pos){ len = _size - pos;} string sub; sub.reserve(len);for(size_t i=0;i<len;++i){ sub+= _str[pos + i];}return sub;}voidtest_string1(){ string s1; string s2("hello world"); cout << s1.c_str()<< endl; cout << s2.c_str()<< endl;for(size_t i =0; i < s2.size();++i){ s2[i]+=2;} cout << s2.c_str()<< endl;for(auto e : s2){ cout << e <<" ";} cout << endl; string::iterator it = s2.begin();while(it != s2.end()){ cout <<*it <<" ";++it;}}voidtest_string2(){ string s1("hello wwww"); s1 +='x'; cout << s1.c_str()<< endl; s1.insert(3,"sss"); cout << s1.c_str()<< endl; s1.erase(3,2); cout << s1.c_str()<< endl;}voidtest_string3(){ string s("666.cpp.zip"); size_t pos =s.find('.'); string suffix = s.substr(pos); cout << suffix.c_str()<< endl; string copy(s); cout << copy.c_str()<< endl;}booloperator<(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())<0;}booloperator==(const string& s1,const string& s2){returnstrcmp(s1.c_str(), s2.c_str())==0;}booloperator>(const string& s1,const string& s2){return!(s1 < s2 || s1 == s2);}booloperator<=(const string& s1,const string& s2){return s1 < s2 || s1 == s2;}booloperator>=(const string& s1,const string& s2){return!(s1 < s2);}booloperator!=(const string& s1,const string& s2){return!(s1 == s2);} ostream&operator<<(ostream& out,const string& s){for(auto ch : s){ out << ch;}return out;} istream&operator>>(istream& in, string& s){ s.clear();constint N =256;char buff[N];int i =0;char ch;//in >> ch; cin默认提取不到空格跟换行 ch = in.get();while(ch !=' '&& ch !='\n'){ buff[i++]= ch;if(i == N -1){ buff[i]='\0'; s += buff; i =0;} s += ch;//in >> ch; ch = in.get();if(i >0){ buff[i]='\0'; s += buff;}}return in;}voidtest_string4(){ string s1("hello"); cin >> s1 ; cout << s1<<endl; cout <<"he";}}intmain(){ bit::test_string4();}

string.h

#pragmaonce#include<iostream>#include<assert.h>#include<string.h>usingnamespace std;namespace bit {classstring{public://string()// : _str(new char[1]{'\0'})//不能给nullptr 因为会对空指针解引用// , _size(0)// ,_capacity(0)//{}//短小频繁调用的函数,可以直接放到类里面,默认是inlinetypedefchar* iterator;//迭代器就是对各类型的封装 iterator begin(){return _str;} iterator end(){return _str + _size;}typedefconstchar* const_iterator; const_iterator begin()const{return _str;} const_iterator end()const{return _str + _size;} size_t capacity()const{return _capacity;}voidreserve(size_t n);voidpush_back(char ch);voidappend(constchar* str); string&operator+=(char ch); string&operator+=(constchar* str);voidinsert(size_t pos,char ch);voidinsert(size_t pos,constchar* str);voiderase(size_t pos, size_t len = npos); size_t find(char ch, size_t pos =0); size_t find(constchar* str, size_t pos =0); string substr(size_t pos =0, size_t len = npos);string(constchar* str="")//""就相当于一个\0{ _size =strlen(str); _capacity = _size;//capacity不加1,因为capacity不包含\0 _str =newchar[_capacity +1];//空间多开一个放\0strcpy(_str, str);//会把\0也拷贝过去}//s2(s1) 拷贝构造//传统写法/*string(const string& s) { _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; }*///s2(s1) tmp是s2//现代写法string(const string& s){ string tmp(s._str);//默认构造swap(tmp);//把tmp和s2交换}//s2=s1 赋值//传统写法/*string& operator=(const string& s) { if (this != &s) { delete[] _str; _str = new char[s._capacity + 1]; _size = s._size; _capacity = s._capacity; } return *this; }*///s1=s3//string& operator=(const string& s)//{// if (this != &s)// {// //把s3的内容都给tmp.然后s1与tmp交换// //string tmp(s._str); 调用默认构造// string tmp(s);//调用拷贝构造// swap(tmp);// }// //而且tmp出了作用域自动销毁// return *this;//}//最新版本的赋值 string&operator=(string tmp)//拷贝构造必须用引用,不然会无穷递归 但赋值不用{swap(tmp);return*this;}voidswap(string& s){ std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity);}~string(){delete[] _str; _str =nullptr; _capacity = _size =0;}voidclear(){ _str[0]='\0'; _size =0;}constchar*c_str()const{return _str;} size_t size()const{return _size;}char&operator[](size_t pos){assert(pos < _size);return _str[pos];}constchar&operator[](size_t pos)const{assert(pos < _size);return _str[pos];}private:char* _str=nullptr; size_t _size=0; size_t _capacity=0;//下列这也算定义staticconst size_t npos=-1;//静态成员类外初始化,但是这里可以也直接给,static const才可以在这里直接初始化,而且只有整型可以这么玩};//const size_t npos = -1;}

(tmp);

 return *this; } void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } ~string() { delete[] _str; _str = nullptr; _capacity = _size = 0; } void clear() { _str[0] = '\0'; _size = 0; } const char* c_str() const { return _str; } size_t size() const { return _size; } char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; } private: char* _str=nullptr; size_t _size=0; size_t _capacity=0; //下列这也算定义 static const size_t npos=-1;//静态成员类外初始化,但是这里可以也直接给,static const才可以在这里直接初始化,而且只有整型可以这么玩 }; //const size_t npos = -1; 

}

Read more

1Panel 安装与使用全指南:从部署到实战运维

1Panel 安装与使用全指南:从部署到实战运维

1Panel 安装与使用全指南:从部署到实战运维 1Panel 作为一款开源、现代化的 Linux 服务器运维管理面板,凭借容器化架构、简洁界面和丰富功能,成为替代传统面板的优选工具。本文将从安装准备、部署步骤、功能使用到进阶技巧,全方位带你掌握 1Panel 的实战运维能力。 一、1Panel 简介:为什么选择它? 1Panel 基于 Go 语言开发,采用 Docker 容器化技术,实现了服务器管理的「轻量化、低侵入、高安全」。核心优势包括: * 可视化运维:通过 Web 界面管理服务器资源、应用部署、域名 SSL 等,无需复杂命令; * 容器化隔离:所有应用运行在 Docker 容器中,避免环境冲突,降低运维风险; * 丰富生态:内置

By Ne0inhk
[特殊字符]颠覆MCP!Open WebUI新技术mcpo横空出世!支持ollama!轻松支持各种MCP Server!Cline+Claude3.7轻松开发论文检索MCP Server!

[特殊字符]颠覆MCP!Open WebUI新技术mcpo横空出世!支持ollama!轻松支持各种MCP Server!Cline+Claude3.7轻松开发论文检索MCP Server!

🔥🔥🔥本篇笔记所对应的视频:🚀颠覆MCP!Open WebUI新技术mcpo横空出世!支持ollama!轻松支持各种MCP Server!Cline+Claude3.7轻松开发MCP服务_哔哩哔哩_bilibili Open WebUI 的 MCPo 项目:将 MCP 工具无缝集成到 OpenAPI 的创新解决方案 随着人工智能工具和模型的快速发展,如何高效、安全地将这些工具集成到标准化的 API 接口中成为了开发者面临的重要挑战。Open WebUI 的 MCPo 项目(Model Context Protocol-to-OpenAPI Proxy Server)正是为了解决这一问题而设计的。本文将带您深入了解 MCPo 的功能、优势及其对开发者生态的影响。 什么是 MCPo? MCPo 是一个简单、可靠的代理服务器,能够将任何基于 MCP 协议的工具转换为兼容

By Ne0inhk
Qwen3+Qwen Agent 智能体开发实战,打开大模型MCP工具新方式!(一)

Qwen3+Qwen Agent 智能体开发实战,打开大模型MCP工具新方式!(一)

系列文章目录 一、Qwen3+Qwen Agent 智能体开发实战,打开大模型MCP工具新方式!(一) 二、Qwen3+Qwen Agent +MCP智能体开发实战(二)—10分钟打造"MiniManus" 前言 要说最近人工智能界最火热的开源大模型,必定是阿里发布不久的Qwen3系列模型。Qwen3模型凭借赶超DeepSeek-V3/R1的优异性能,创新的混合推理模式,以及极强的MCP能力迅速成为AI Agent开发的主流基座模型。大家可参考我的文章一文解析Qwen3大模型详细了解Qwen3模型的核心能力。有读者私信我: “Qwen3官网特地强调增强了Agent和代码能力,同时加强了对MCP的支持,那么我该如何利用Qwen3快速开发MCP应用呢?” 这就就需要使用我们今天的主角——Qwen官方推荐的开发工具Qwen-Agent ,本期分享我们就一起学习快速使用Qwen3+QwenAgent 接入MCP服务端,快速开发AI Agent应用! 一、注册 Qwen3 API-Key 本次分享通过阿里云百炼大模型服务平台API Key请求方式调用Qwen3大模型,获取服务平台

By Ne0inhk