C++ string的模拟实现

Hello!!大家早上中午晚上好,昨天复习了string的使用,今天来模拟实现一下string!!!

一、string的框架搭建

1.1首先我们需要一个string的头文件用来做变量、函数、类等声明;再需要一个test文件来做测试,还需要一个文件来实现方法;

包一下头文件ok初步工作完成;

1.2明确string需要用到的成员变量以及成员函数

成员变量:

①必不可少的一个指向字符串的char*指针;(注意这里不能设置为const char*)

②字符串存储的有效数据size_t size;(有效数据指的时'\0'之前的数据)

③字符串存储的那块空间的容量size_t capacity;

④一个无符号最大值npos(用来判断);

成员函数:

①string的构造函数;包括用字符串构造、用string对象构造、用n个字符构造、用迭代器区间构造;实现常用的几个即可;

②string的析构函数;(因为涉及深拷贝所以要写)

③string的运算符重载函数;包括赋值运算符重载,比较小于、大于、等于、不等于运算重载函数,还有下标访问的运算符重载函数,流插入流提取运算符重载函数;

普通成员函数:

①开空间并初始化的 resize函数;

②开空间不初始化的reserve函数;

③插入单个字符的push_back函数;

④插入字符串的+=运算符重载函数;

⑤某个位置插入n个字符的insert函数;

⑥某个位置删除n个字符的erase函数;

⑦获取string有效数据的size()函数;

⑧获取string存储空间大小的capacity()函数;

⑨清理字符串的clear函数;

⑩从某个位置开始查找某个字符的find函数;

11、从某个位置开始截取长度为len的字符串;

12、获取_str的c_str函数;

还有一个需要用到的是迭代器;

1.3声明到头文件

由于模拟实现的string类的名字会跟std里的string冲突,所以需要namespace一个命名空间;

cpp 复制代码
#pragma once
#include <iostream>
using namespace std;
//string的模拟实现
namespace ldc
{
	struct string
	{
		typedef char* iterator;
		typedef const char* const_iterator;
	public:
		string(const char* str);
		string(const string& s);
		string(size_t n, char c);
		string(const_iterator it1, const_iterator it2);
		~string();
		string& operator=(const string& s);
		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;
		char operator[](size_t pos);
		const char operator[](size_t pos)const;
		void resize(size_t n, char c='\0');
		void reserve(size_t n);
		void push_back(char c);
		string& operator+=(const char* str);
		string& opeartor += (const string & s);
        string& ldc::string::operator+=(char ch);

		void insert(size_t pos, size_t n, char c);
		void erase(size_t pos, size_t n=npos);
		size_t size();
		size_t capacity();
		void clear();
		size_t find(char c, size_t pos = 0);
		size_t find(const char* str, size_t pos = 0);
		string& substr(size_t pos = 0, size_t len = npos);
        const char* c_str const();
	private:
		 char* _str;
		size_t _size;
		size_t _capacity;
		const static size_t npos=-1;
	};
    ostream& operator<<(ostream& out, const string& s);//流提出插入重载
	istream& operator>>(istream& in, string& s);//在类外重载不存在this
}

这里注意:npos用来做判断,当调用erase在pos位置删除n个字符时,如果不传参n默认为npos(无符号最大值),表示从pos位置开始往后的全删除;(同理substr也一样);

二、开始实现

2.1构造函数的实现
cpp 复制代码
 #define _CRT_SECURE_NO_WARNINGS 1
#include "string3_20.h"
#include <string.h>
//string的方法实现
//构造函数
ldc::string::string(const char* str="")
{
	size_t len = strlen(str);
	_size = len;
	_capacity = _size;
	_str = new char[_capacity + 1];//这里+1因为要保留一个位置存放'\0',空串也有'\0'
	memcpy(_str, str, len + 1);
}
ldc::string::string(const string& s)
{
	_size = s._size;
	_capacity = s._capacity;
	_str = new char[_capacity + 1];
	memcpy(_str, s._str, _capacity + 1);
}
ldc::string::string(size_t n, char c)
{
	_size = n;
	_capacity = _size;
	_str = new char[_capacity + 1];
	for (size_t i = 0; i < n; i++)
	{
		_str[i] = c;
	}
	_str[n] = '\0';
}
ldc::string::string(const_iterator it1, const_iterator it2)
{
	_size = it2 - it1;
	_capacity = _size;
	_str = new char[_capacity + 1];
	memcpy(_str, it1, _size);
	_str[_size] = '\0';
}

顺便把流插入重载和c_str也实现了(好测试)

cpp 复制代码
const char* ldc::string::c_str()const
{
	return _str;
}
ostream& ldc::operator<<(ostream& out, const string& s)
{
	
	out <<s.c_str();
	return out;
}

测试:

cpp 复制代码
 #define _CRT_SECURE_NO_WARNINGS 1
//string的测试
#include "string3_20.h"
int main()
{
	ldc::string s1("hello world!!"); //字符串构造
	const char* ch = "welcome";  
	ldc::string s2(ch);
	cout << s1 << endl;
	cout << s2 << endl;
    char ch2[10] = { "yes!!!" };
    ldc::string s3(ch2, ch2 + 3);//区间构造
    cout << s3 << endl;
    ldc::string s4(s3);//拷贝构造
    cout<<s4<<endl;
	return 0;
}

ok没问题!接下来实现以下赋值重载跟析构;

2.2赋值重载与析构函数
cpp 复制代码
ldc::string& ldc::string::operator=(const string& s)
{
	_size = s._size;
	_capacity = s._capacity;
	_str = new char[_capacity + 1];
	memcpy(_str, s._str, _capacity + 1);
     return *this;
}
ldc::string::~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

测试:

cpp 复制代码
int main()
{
	ldc::string s1("hello");
	ldc::string s2("welcome");
	s1 = s2;
	cout << s1 << endl;
	return 0;
}

没问题,接下来实现普通成员函数;

2.3普通成员函数的实现
cpp 复制代码
//比较、下标访问运算符重载
bool ldc::string::operator>(const string& s)const
{
	//比较有很多种方法,可以用operator[]比也可以获取str后再比,这里用最简洁的方法
	//库里的memcmp内存比较函数
	int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);//先比短
	return ret == 0 ? _size > s._size:ret > 0;//再比长,如果相等ret为正数表示str1>str2
}
bool ldc::string::operator<=(const string& s)const
{
	return !(*this > s);
}
bool ldc::string::operator<(const string& s)const
{
	return !(*this >= s);
}
bool ldc::string::operator>=(const string& s)const
{
	return (*this > s) || (*this == s);
}
bool ldc::string::operator==(const string& s)const
{
	return _size == s._size && memcmp(_str, s._str, _size) == 0;
}
bool ldc::string::operator!=(const string& s)const
{
	return !(*this == s);
}

char ldc::string::operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
const char ldc::string::operator[](size_t pos)const
{
	assert(pos < _size);
	return _str[pos];
}

测试:

cpp 复制代码
void test1()
{
	ldc::string s1("hello");
	ldc::string s2("helloxxx");
	ldc::string s3("welcome");
	ldc::string s4("welcome");
	cout << (s1 > s2) << " ";
	cout << (s1 >= s2) << " ";
	cout << (s1 < s2) << " ";
	cout << (s1 <= s2) << " ";
	cout << (s3 == s4) << " ";
	cout << (s3!= s2) << " ";
	cout << s1[2] << " ";
	s1[0]='Y';
	cout << s1 << " ";
	//const ldc::string s5("beutiful!");
	//s5[0] = 'B';
}
int main()
{
	test1();
	return 0;
}

OK没问题接着实现扩容:

2.4扩容实现
cpp 复制代码
//只开空间不初始化
void ldc::string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];//假设开100个空间,需要存放100个数据,第101个位置需存放结束标识符'\0'
		memcpy(tmp, _str, _size + 1);//连'\0'一起拷贝,不然开完空间后的数据没有结束标志符
		delete[] _str;//释放旧空间
		_str = tmp;//指向新空间
		_capacity = n;//注意只需要修改_capacity,_size有效数据始终没变
	}
}

//开空间并初始化
void ldc::string::resize(size_t n, char c = '\0')
{
	if (n < _size)
	{
		_size = n;
		_str[_size] = '\0';
	}
	else
	{
		reserve(n);
		for (size_t i=_size;i<n;i++)
		{
			_str[i] = c;
		}
		_str[n] = '\0';
		_size = n;
	}
}

测试:

cpp 复制代码
void test2()
{
	ldc::string s1("hello");
	s1.resize(20, 'x');
	cout << s1 << endl;
	s1.resize(3);
	cout << s1 << endl;
}
int main()
{
	//test1();
	test2();
	return 0;
}

ok没问题下一步,reserve后面测;

2.5增删查改实现
cpp 复制代码
void ldc::string::push_back(char c)
{
	if (_size == _capacity)
	{
		size_t n = _capacity == 0 ? 4 : _capacity * 2;//二倍扩容
		reserve(n);
	}
	_str[_size] = c;
	++_size;
	_str[_size] = '\0';
}
ldc::string& ldc::string::operator+=(const char* str)
{
	size_t len = strlen(str);
	reserve(_size + len);//复用reserve开空间
	memcpy(_str + _size, str, len + 1);//'\0'一起拷贝过去
	_size += len;
	return *this;
}
ldc::string& ldc::string::operator+=(char ch)
{
	push_back(ch);
	return *this;
}
ldc::string&ldc::string::operator+= (const string & s)
{
	reserve(_size + s._size);
	memcpy(_str + _size, s._str, s._size + 1);
	_size += s._size;
	return *this;
}
void ldc::string::insert(size_t pos, size_t n, char c)
{
	assert(pos <= _size);
	reserve(_size + n);
	if (pos==_size)//尾插
	{
		while (n--)
		{
			push_back(c);
		}
	}
	else
	{
		char* begin = _str + _size;
		char* newbegin = _str + pos;
		char* end = _str + _size + n;
		while (begin>=newbegin)
		{
			*end = *begin;
			--end;
			--begin;
		}
		int i = n;
		while (i--)
		{
			_str[pos] = c;
			++pos;
		}
		_size += n;
	}
	
}
void ldc::string::erase(size_t pos, size_t n)
{
	assert(pos < _size);
	if (n == npos || pos + n > _size)
	{
		_str[pos] = '\0';
	}
	else
	{
		size_t end = _size;
		size_t begin = pos + n;
		while (begin != end)
		{
			_str[pos] = _str[begin];
			++pos; ++begin;
		}
	}
	_size -= n;
	_str[_size] = '\0';
}
size_t ldc::string::find(char c, size_t pos)
{
	assert(pos < _size);
	while (pos != _size)
	{
		if (_str[pos] == c)
		{
			return pos;
		}
		++pos;
	}
	return npos;
}
size_t ldc::string::find(const char* str, size_t pos)
{
	//直接用strstr函数
	char* ptr = strstr(_str + pos, str);
	if (ptr)
	{
		return ptr - _str;
	}
	else
		return npos;
}

测试:

cpp 复制代码
void test3()
{
	ldc::string s1("hello world!!");
	s1.insert(5, 3, 'X');
	cout << s1 << endl;
	s1.insert(0, 3, 'Y');
	cout << s1 << endl;
	s1.insert(19, 2, 'Z');
	cout << s1 << endl;
	s1.erase(0, 3);
	cout << s1 << endl;
	s1.erase(5, 4);
	cout << s1 << endl;
	s1.erase(10, -1);
	cout << s1 << endl;

	cout << s1.find('h')<<endl;
	cout << s1.find("world") << endl;
	cout << s1.find('l', 3) << endl;
}

没啥问题,接着把剩下的都实现了;

三、剩余完善

3.1substr实现
cpp 复制代码
ldc::string ldc::string::substr(size_t pos, size_t len)
{
	assert(pos < _size);
	size_t n = len;
	if (n == npos || pos + n > _size)
	{
		n = _size - pos;
	}
	string temp;
	temp.reserve(n);
	for (size_t i = 0; i < n; i++)
	{
		temp += _str[pos + i];
	}
	return temp;
}
3.2其他实现
cpp 复制代码
size_t ldc::string::size()const
{
	return _size;
}
size_t ldc::string::capacity()const
{
	return _capacity;
}
void ldc::string::clear()
{
	_str[0] = '0';
	_size = 0;
}
istream& ldc::operator>>(istream& in, string& s)
{
	s.clear();
	char ch = in.get();
	// 处理前缓冲区前面的空格或者换行
	while (ch == ' ' || ch == '\n')
	{
		ch = in.get();
	}
	//in >> ch;
	char buff[128];
	int i = 0;
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		//in >> ch;
		ch = in.get();
	}
	if (i != 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}
3.3测试
cpp 复制代码
void test4()
{
	ldc::string s1("hello");
	ldc::string s2(s1.substr(0, 2));
	cout << s1 << endl;
	cout << s2 << endl;
	cout <<"开空间前:" << s2.capacity() <<" ," << s2.size() << endl;
	s2.reserve(40);
	cout <<"开空间后:"<< s2.size() << ", " << s2.capacity() << endl;
	cin>>s2;
	cout << s2<<endl;
	cout << "流插入后:" << s2.size() << " " << s2.capacity() << endl;
}

OK!!到这里string的基本功能已经实现了!!如果您觉得有所收获,记得点赞收藏+关注哦!!谢谢!!!

咱下期见!!!

相关推荐
QQ828929QQ3 分钟前
Spring Boot拦截器(Interceptor)与过滤器(Filter)深度解析:区别、实现与实战指南
java·spring boot·后端
John_ToDebug4 分钟前
深入解析 Service Worker 在 Chrome 扩展中的应用
c++·chrome·性能优化
喵帕栞23 分钟前
C语言贪吃蛇实现
c语言·笔记·链表
一问三不知_27 分钟前
pyqt5报错:qt.qpa.plugin: Could not find the Qt platform plugin “xcb“(已解决)
开发语言·python·qt·ubuntu·conda·bug
小白的高手之路33 分钟前
Pytorch中的数据加载
开发语言·人工智能·pytorch·python·深度学习·机器学习
法迪1 小时前
Windows搭建免翻墙的BatteryHistorian
windows·功耗
isllxiao1 小时前
常见中间件漏洞(tomcat)
java·tomcat
述雾学java1 小时前
JavaWeb,Tomcat基本思想,手写Tomcat
java·tomcat·java核心基础
ok0601 小时前
JavaScript(JS)单线程影响速度
开发语言·javascript·ecmascript
液态不合群1 小时前
C# 中比较实用的关键字,基础高频面试题!
开发语言·c#