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

C++——string的模拟实现(下)

目录

成员函数

3.4 修改操作

(3)insert()函数

(4)pop_back()函数

(5)erase()函数

(6)swap()函数

3.5 查找操作

(1)find()函数

 (2)substr()函数

3.6 重载函数

(1)operator=赋值函数

(2)其他比较函数

(3)流插入和流提取

完整代码

结束语


第一篇链接:C++——string的模拟实现(上)

成员函数

3.4 修改操作
(3)insert()函数

insert() 函数用于在字符串的指定位置插入一个字符或字符串。

注意事项:

在移动字符串内容时,需要确保不会覆盖或丢失任何字符。

使用 memcpy() 时,要确保源和目标内存区域不重叠,否则可能导致未定义行为。

string.h:

	// insert()函数 void insert(size_t pos, char ch);	//插入字符void insert(size_t pos, const char* str);	//插入字符串

string.cpp:

// insert()函数
// 插入字符
void string::insert(size_t pos, char ch)
{assert(pos <= _size);if (_capacity == _size){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}size_t end = _size + 1;// 挪动元素while (pos < end){_str[end] = _str[end - 1];end--;}_str[pos] = ch;		//将ch放置到pos这个位置++_size;			//更新_size
}// 插入字符串
void string::insert(size_t pos, const char* str)
{assert(pos <= _size);size_t len = strlen(str);	// 要插入的字符串长度if (_size + len > _capacity){reserve(_size + len);}// 从后向前移动字符串内容以腾出空间  for (size_t i = _size; i >= pos; --i){_str[i + len] = _str[i];}// 复制新字符串到指定位置  memcpy(_str + pos, str, len);// 添加新的字符串结束符  _str[_size + len] = '\0';// 更新字符串长度  _size += len;
}

来个简单的代码测试一下:

(4)pop_back()函数

pop_back() 函数用于移除字符串末尾的字符。

实现:将字符串末尾的字符设置为 '\0'。 更新字符串长度 _size。

string.h:

// pop_back()函数
void pop_back();

string.cpp:

// pop_back()函数
void string::pop_back()
{_str[_size - 1] = '\0';--_size;
}
(5)erase()函数

erase() 函数用于从字符串中移除指定位置的字符或子字符串。

string.h:

// erase()函数
void erase(size_t pos = 0, size_t len = npos);

string.cpp:

// erase()函数
void string::erase(size_t pos,size_t len)
{assert(pos < _size);				 if (len == npos || len >= _size - pos){_str[pos] = '\0';		// 位置pos置为'\0'_size = pos;			// 有效元素个数为pos个}else	// len小于后面的字符个数{// 将后面的字符拷贝到pos位置strcpy(_str + pos, _str + pos + len);_size -= len;			// 更新有效元素}
}

erase()需要额外定义一个类成员变量npos来实现,它为无符号数的-1,一般为整型的最大值。

string.h:

public:static const size_t npos;

string.cpp:

const size_t string::npos = -1;

简单的对上面两个函数进行测试:

(6)swap()函数

swap() 函数用于交换两个字符串的内容。

string.h:

// swap()函数
void swap(string& str);

string.cpp:

// swap()函数
void string::swap(string& str)
{// 这里我们调用std库中的swap()函数std::swap(_str, str._str);std::swap(_capacity, str._capacity);std::swap(_size, str._size);
}

测试:

3.5 查找操作
(1)find()函数

find() 函数用于在字符串中查找指定字符或子字符串的位置。

我们在这里实现从指定位置开始向后遍历查找指定字符或者子字符串。

如果找到,则返回子字符串在字符串中的起始位置;否则返回 npos。

string.h:

// find()函数
size_t find(char ch, size_t pos);
size_t find(const char* str, size_t pos);

string.cpp:

// find()函数
// 寻找字符
size_t string::find(char ch, size_t pos)
{for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}// 从pos开始寻找字符串
size_t string::find(const char* str, size_t pos)
{char* p = strstr(_str + pos, str);if (p){return p - _str;}else{return npos;}
}
 (2)substr()函数

substr() 函数用于获取字符串的子字符串。

string.h:

// substr()函数
string substr(size_t pos = 0, size_t len = npos);

string.cpp:

// substr()函数
string string::substr(size_t pos, size_t len)
{string str;if (len == npos || len >= _size - pos){for (size_t i = pos; i < _size; i++){str += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){str += _str[i];}}return str;
}
3.6 重载函数

在C++中,函数重载允许我们为同一函数名创建多个版本,这些版本可以有不同的参数列表(参数的数量或类型不同)。通过这种方式,我们可以使代码更加简洁和易于理解。

(1)operator=赋值函数

赋值运算符(operator=)用于将一个对象的内容复制到另一个对象中。对于自定义的字符串类,我们需要自己实现这个运算符,以确保正确地管理内存。

string.h:

// operator=赋值函数
string& operator=(const string& s);

string.cpp:

// operator= 函数
string& string::operator=(const string& str)
{if (this != &str){char* tmp = new char[str._capacity + 1];strcpy(tmp, str._str);delete[] _str;_str = tmp;_size = str._size;_capacity = str._capacity;}return *this;
}

来测试一下:

这里我们可以对以上的代码进行优化:

// operator= 函数
string& string::operator=(const string& str)
{if (this != &str){string tmp(str._str);	// 调用构造函数swap(tmp);				// 将tmp与this交换}return *this;
}

原始实现中,我们首先检查自赋值(即对象试图将自己赋值给自己),然后分配足够的内存来存储新字符串,复制内容,并释放旧内存。这种方法虽然有效,但可能会导致性能问题,特别是当字符串很长时,因为涉及到多次内存分配和释放。

优化后的实现采用了“拷贝-交换”技术。这种方法通过创建一个临时对象来存储要赋值的字符串,然后使用swap函数交换临时对象和当前对象的内容。由于swap函数通常实现得非常高效(只需交换指针),这种方法可以显著提高性能,并减少内存分配的次数。

(2)其他比较函数

每个比较运算符都基于strcmp函数的返回值来实现。

这些函数之间是可以复用的。

string.h:

// 比较函数
bool operator<(const string& s)const;
bool operator<=(const string& s)const;
bool operator>(const string& s)const;
bool operator>=(const string& s)const;
bool operator==(const string& s)const;
bool operator!=(const string& s)const;

string.cpp:

// 比较函数
bool string::operator<(const string& str) const
{return strcmp(_str, str._str) < 0;
}
bool string::operator<=(const string& str) const
{return *this < str || *this == str;
}
bool string::operator>(const string& str) const
{return !(*this <= 0);
}
bool string::operator>=(const string& str) const
{return !(*this <= str);
}
bool string::operator==(const string& str)const
{return strcmp(_str, str._str) == 0;
}
bool string::operator!=(const string& str)const
{return !(*this == str);
}void string::clear()
{_str[0] = '\0';_size = 0;
}
(3)流插入和流提取

我们接下来试着实现流插入和流提取:

注意,我们不能将这两个函数定义放在string类里

在 C++ 中,当我们使用 std::cout << d1 或 std::cin >> d1 这样的表达式时,左侧的 std::cout 或 std::cin 是对象,而右侧的 d1 是我们想要输出或输入的数据。运算符函数需要能够接受这两个对象作为参数。 如果 << 或 >> 运算符被定义为 std::string 的成员函数,那么它们将需要额外的 this 指针来访问类的成员变量,这将导致需要三个参数(this 指针,左操作数,右操作数),这与标准的运算符用法不兼容。

string.h:

//流插入和流提取
istream& operator>>(istream& is, string& str);
ostream& operator<<(ostream& os, const string& str);

string.cpp:

// 流输入
istream& operator>>(istream& is, string& str)
{// 清除字符串 str 中的现有内容  str.clear();char ch;// 从输入流 is 中读取第一个字符到 ch 中  ch = is.get();// 定义一个字符数组(缓冲区)buffchar buff[128];size_t i = 0;// 循环读取字符,直到遇到空格或换行符为止  while (ch != ' ' && ch != '\n'){// 将字符 ch 存储到缓冲区 buff 的当前位置  buff[i++] = ch;  if (i == 127){// 在缓冲区末尾添加字符串结束符 '\0'buff[i++] = '\0';str += buff;// 重置索引 i,为下一轮存储字符做准备  i = 0;}// 从输入流中读取下一个字符到 ch 中  ch = is.get();}// 循环结束后,检查缓冲区中是否还有未处理的字符  if (i > 0){// 在缓冲区末尾添加字符串结束符 '\0'  buff[i] = '\0';// 将缓冲区的内容追加到字符串 str 中  str += buff;}return is;
}
// 流提取
ostream& operator<<(ostream& os, const string& str)
{for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;
}

测试一下:

完整代码

string.h:

#include<iostream>
#include<assert.h>
#include<stdbool.h>
using namespace std;namespace My_string
{class string{public:typedef char* iterator;			//将char*重命名为iteratortypedef const char* const_iterator;//const版本的iteratorconst_iterator begin() const;	//提供const_iterator begin()函数const_iterator end() const;		//提供const_iterator end()函数//非const版本的iteratoriterator begin();				//提供iterator begin()函数iterator end();					//提供iterator end()函数string(const char* str = " ");	//构造函数~string();						//析构函数string(const string& str);		//拷贝构造函数const char* c_str() const;		// c_str()函数size_t size() const;			// size()函数size_t capacity() const;		// capacity()函数bool empty() const;				// empty()函数// resize()函数void resize(size_t n, char ch = '\0');// 非const版本char& operator[](size_t pos);	//operator[]函数// const版本const char& operator[](size_t pos)const;// 预留空间void reserve(size_t n);// 尾插一个字符void push_back(char ch);// 尾插一个字符串void append(const char* str);//operator+=函数可以构成重载,函数名相同,参数不同string& operator+=(char ch);			// 字符相加string& operator +=(const char* str);	// 字符串相加// insert()函数 void insert(size_t pos, char ch);	//插入字符void insert(size_t pos, const char* str);	//插入字符串// erase()函数void erase(size_t pos = 0, size_t len = npos);// pop_back()函数void pop_back();// find()函数size_t find(char ch, size_t pos);size_t find(const char* str, size_t pos);// substr()函数string substr(size_t pos = 0, size_t len = npos);// operator=赋值函数string& operator=(const string& s);// swap()函数void swap(string& str);// 比较函数bool operator<(const string& s)const;bool operator<=(const string& s)const;bool operator>(const string& s)const;bool operator>=(const string& s)const;bool operator==(const string& s)const;bool operator!=(const string& s)const;// clear()函数void clear();public:static const size_t npos;private:char* _str;			// 指向字符串的指针size_t _size;		// 有效字符个数size_t _capacity;	// 有效空间个数};//流插入和流提取istream& operator>>(istream& is, string& str);ostream& operator<<(ostream& os, const string& str);
}

string.cpp:

#include"string.h"
namespace My_string 
{const size_t string::npos = -1;string::iterator string::begin(){return _str;}string::iterator string::end(){return _str + _size;}string::const_iterator string::begin() const{return _str;}string::const_iterator string::end() const{return _str + _size;}// 构造函数string::string(const char* str){_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];		// +1用于储存'\0'strcpy(_str, str);}// 析构函数string::~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}// 拷贝构造函数(1)string::string(const string& str){_str = new char[str._capacity + 1];	//额外多给一个空间,用于存放'/0'strcpy(_str, str._str);		//拷贝数据_capacity = str._capacity;	//设置容量_size = str._size;			//设置有效数据个数}// 拷贝构造函数(2)//string::string(const string& str)//{//	string tmp(str._str);//	std::swap(tmp._str, _str);//	std::swap(tmp._size, _size);//	std::swap(tmp._capacity, _capacity);//}// 拷贝构造函数(3)//string::string(const string& str)//{//	string tmp(str._str);//	swap(tmp);			// 这里的swap我们接下来会定义//}// c_str()函数const char* string::c_str() const{return _str;}// size()函数size_t string::size() const{return _size;}// capacity()函数size_t string::capacity() const{return _capacity;}// empty()函数bool string::empty() const{return _size == 0;}// operator[]函数char& string::operator[](size_t pos){assert(pos < _size);return _str[pos];}// const版本const char& string::operator[](size_t pos)const{assert(pos < _size);return _str[pos];}// 预留空间void string::reserve(size_t n){if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}}// resize()函数void string::resize(size_t n, char ch){if (n > _size){if (n > _capacity){reserve(n);}// 使用 memset 函数将字符 ch // 填充到新添加的空间中memset(_str + _size, ch, n - _size);}_size = n;_str[n] = '\0';}//尾插一个字符void string::push_back(char ch){if (_capacity == _size){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}_str[_size] = ch;_str[_size + 1] = '\0';_size++;}//尾插一个字符串void string::append(const char* str){size_t len = strlen(str);if (_size + len > _capacity) {reserve(_size + len);}strcpy(_str+_size, str);_size += len;			}string& string::operator+=(char ch){// 调用push_back()函数push_back(ch);		return *this;}string& string::operator+=(const char* str){append(str);return *this;}// insert()函数// 插入字符void string::insert(size_t pos, char ch){assert(pos <= _size);if (_capacity == _size){size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;reserve(newcapacity);}size_t end = _size + 1;// 挪动元素while (pos < end){_str[end] = _str[end - 1];end--;}_str[pos] = ch;		//将ch放置到pos这个位置++_size;			//更新_size}// 插入字符串void string::insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);	// 要插入的字符串长度if (_size + len > _capacity){reserve(_size + len);}// 从后向前移动字符串内容以腾出空间  for (size_t i = _size; i >= pos; --i){_str[i + len] = _str[i];}// 复制新字符串到指定位置  memcpy(_str + pos, str, len);// 添加新的字符串结束符  _str[_size + len] = '\0';// 更新字符串长度  _size += len;}// erase()函数void string::erase(size_t pos,size_t len){assert(pos < _size);				 if (len == npos || len >= _size - pos){_str[pos] = '\0';		// 位置pos置为'\0'_size = pos;			// 有效元素个数为pos个}else	// len小于后面的字符个数{// 将后面的字符拷贝到pos位置strcpy(_str + pos, _str + pos + len);_size -= len;			// 更新有效元素}}// pop_back()函数void string::pop_back(){_str[_size - 1] = '\0';--_size;}// find()函数// 寻找字符size_t string::find(char ch, size_t pos){for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return npos;}// 从pos开始寻找字符串size_t string::find(const char* str, size_t pos){char* p = strstr(_str + pos, str);if (p){return p - _str;}else{return npos;}}// substr()函数string string::substr(size_t pos, size_t len){string str;if (len == npos || len >= _size - pos){for (size_t i = pos; i < _size; i++){str += _str[i];}}else{for (size_t i = pos; i < pos + len; i++){str += _str[i];}}return str;}// operator= 函数//string& string::operator=(const string& str)//{//	if (this != &str)//	{//		char* tmp = new char[str._capacity + 1];//		strcpy(tmp, str._str);//		delete[] _str;//		_str = tmp;//		_size = str._size;//		_capacity = str._capacity;//	}//	return *this;//}// operator= 函数string& string::operator=(const string& str){if (this != &str){string tmp(str._str);	// 调用构造函数swap(tmp);				// 将tmp与this交换}return *this;}// swap()函数void string::swap(string& str){// 这里我们调用std库中的swap()函数std::swap(_str, str._str);std::swap(_capacity, str._capacity);std::swap(_size, str._size);}// 比较函数bool string::operator<(const string& str) const{return strcmp(_str, str._str) < 0;}bool string::operator<=(const string& str) const{return *this < str || *this == str;}bool string::operator>(const string& str) const{return !(*this <= 0);}bool string::operator>=(const string& str) const{return !(*this <= str);}bool string::operator==(const string& str)const{return strcmp(_str, str._str) == 0;}bool string::operator!=(const string& str)const{return !(*this == str);}void string::clear(){_str[0] = '\0';_size = 0;}// 流输入istream& operator>>(istream& is, string& str){// 清除字符串 str 中的现有内容  str.clear();char ch;// 从输入流 is 中读取第一个字符到 ch 中  ch = is.get();// 定义一个字符数组(缓冲区)buffchar buff[128];size_t i = 0;// 循环读取字符,直到遇到空格或换行符为止  while (ch != ' ' && ch != '\n'){// 将字符 ch 存储到缓冲区 buff 的当前位置  buff[i++] = ch;  if (i == 127){// 在缓冲区末尾添加字符串结束符 '\0'buff[i++] = '\0';str += buff;// 重置索引 i,为下一轮存储字符做准备  i = 0;}// 从输入流中读取下一个字符到 ch 中  ch = is.get();}// 循环结束后,检查缓冲区中是否还有未处理的字符  if (i > 0){// 在缓冲区末尾添加字符串结束符 '\0'  buff[i] = '\0';// 将缓冲区的内容追加到字符串 str 中  str += buff;}return is;}// 流提取ostream& operator<<(ostream& os, const string& str){for (size_t i = 0; i < str.size(); i++){os << str[i];}return os;}
}

test.cpp:

#include"string.h"
#include<iostream>using namespace std;void test1()
{// 测试构造函数  My_string::string s1("Hello");cout << "s1: " << s1.c_str() << endl;// 测试拷贝构造函数  /*My_string::string s2(s1);cout << "s2 (s1): " << s2.c_str() << endl;	*/
}void test2()
{My_string::string str("Hello");str[0] = 'h';cout << str.c_str() << endl;//使用operator[]函数打印for (size_t i = 0; i < str.size(); i++){cout << str[i] << " ";}cout << endl;
}void test3()
{My_string::string str("hello");for (auto i : str){cout << i << " ";}cout << endl;My_string::string::iterator it1 = str.begin();while (it1 != str.end()){cout << *it1 << " ";++it1;}cout << endl;My_string::string::iterator it2 = str.end();if (it2 != str.begin()) { // 检查避免直接解引用 end()  --it2; // 先移动到一个有效的位置  while (it2 != str.begin()){std::cout << *it2 << " ";--it2;}std::cout << *it2 << " "; // 输出最后一个字符(begin() 之前的字符)  }std::cout << std::endl;
}void test4()
{My_string::string str("hello");cout << str.c_str() << endl;str.push_back('A');cout << str.c_str() << endl;str.append("world");cout << str.c_str() << endl;str += 'C'; cout << str.c_str() << endl;
}void test5()
{My_string::string str("hello world");str.insert(6, 'x');cout << str.c_str() << endl;str.insert(6, "yyyyy");cout << str.c_str() << endl;str.insert(0, 'X');cout << str.c_str() << endl;
}void test6()
{My_string::string str("hello world");str.pop_back();cout << str.c_str() << endl;str.erase(2, 3);cout << str.c_str() << endl;
}void test7()
{My_string::string str("hello world");cout << str.find('l', 0) << endl;cout << str.find("ld", 0) << endl;
}void test8()
{My_string::string str1("hello");My_string::string str2("world");cout << str1.c_str() << endl;cout << str2.c_str() << endl;str1.swap(str2);cout << str1.c_str() << endl;cout << str2.c_str() << endl;
}void test9()
{My_string::string str("hello World");cout << str.substr(2, 2) << endl;cout << str.substr(7, 3) << endl;
}void test10()
{My_string::string str1("hello world");My_string::string str2("xxxxxxxxxxx");str1 = str2;cout << str1.c_str() << endl;cout << str2.c_str() << endl;My_string::string str3("yyyyy");str1 = str3;cout << str1.c_str() << endl;cout << str3.c_str() << endl;
}void test11()
{My_string::string str("hello");str.resize(10, '*');cout << str.c_str() << endl;str.reserve(15);cout << str.c_str() << endl;
}void test12()
{My_string::string str("XXX");cin >> str;cout << str << endl;
}int main()
{//test1();//test2();//test3();//test4();//test5();//test6();//test7();//test8();//test9();//test10();//test11();test12();return 0;
}

结束语

实验周太忙啦!!!一直没时间写。。。

感谢各位大佬的阅读!!!

求点赞收藏评论关注!!!


http://www.mrgr.cn/news/59763.html

相关文章:

  • 【AI探索实践】使用Docker部署ChatGPT Next Web个人智能助手
  • 【HTML】之form表单元素详解
  • 多款云存储平台存在安全漏洞,影响超2200万用户
  • 数据结构之堆和二叉树的简介
  • Node.js初学者指南:搭建HTTP服务器、获取请求信息及响应、变量声明与NPM包管理
  • day02|计算机网络重难点之HTTP请求报文和响应报文、HTTP的请求方式(方法字段)、GET请求和POST请求的区别
  • kubernetes中的ingress-nginx
  • Mybatis中的参数占位符:${...} 、#{...}的区别
  • SD2.0 Specification之响应(Responses)
  • 小样本语义分割(MSDNet网络详解)
  • 【iOS】使用AFNetworking进行网络请求
  • XJ07、消费金融|信贷还款的基本种类及其系统交互
  • 【MySQL】LeeCode高频SQL50题基础版刷题记录(持续更新)
  • 实验干货|电流型霍尔传感器采样设计03-信号调理
  • BGP 12 条选路原则笔记
  • mysql 视图中用变量实现 自增序号
  • Nature 正刊丨利福昔明预防引起对最后一种抗生素达托霉素的耐药性
  • 如何创建一个Vue项目【手把手教会你】
  • 猫头虎 分享:MySQL 中 TEXT 与 LONGTEXT 数据类型详解与使用场景分析
  • C++标准库之std::begin、std::end、std::pre和std::next
  • Maven 项目构建打包,如何引入本地 Jar 包?
  • 【Rust练习】18.特征 Trait
  • 人工智能与深度学习入门
  • 【K8S系列】Kubernetes 中 Service IP 地址和端口不匹配问题及解决方案【已解决】
  • Maven:详解 clean 和 install 命令的使用
  • promise+async/await+任务队列