C++学习之STL学习:string类常用接口的模拟实现

上一篇博客我们学习了string类的常见应用,接下来我们将堆对它们的底层进行深入挖掘,并进行模拟实现

作者个人gitee主页:楼田莉子 (riko-lou-tian) - Gitee.com

感兴趣的可以自行去查看

目录

string实现的前置准备

string的构造/析构实现

string()的模拟实现

[string(const char* str)的模拟实现](#string(const char* str)的模拟实现)

构造的合并

string的拷贝构造函数

赋值重载

string的析构实现

string函数的实现

size()的模拟实现

operator[]的模拟实现

迭代器模拟实现

正向迭代器

const正向迭代器

string的插入函数

reserve

pushback

append

+=运算符重载

insert

成员变量:npos

erase

pop_back

find

substr

clear函数

swap函数

流插入与流提取

流插入

流提取

getline函数

比较函数

<

<=

>

>=

==

!=

源代码


string实现的前置准备

首先我们需要三个文件

string.h:string实现的命名空间、类以及函数声明

string.cpp:string实现的方法

test.cpp:测试文件

string在底层上可以认为是一个字符顺序表。因此在底层上我们也像顺序表那样定义三个变量

cpp 复制代码
char* _str;
size_t _size;
size_t _capacity;

同时在测试文件test.c需要将模拟实现与原装时间进行对比,但是这样就会与我们系统的string冲突。所以我们需要一个命名空间来包装它

cpp 复制代码
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
	//命名空间:不吉波普
{
	class basic_string
	{
	public:


	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
};

命名空间可以在多个文件进行书写。多个文件的命名空间一般被认为是同一个命名空间(相当于被合并了)

我们要在三个文件中都用同一个命名空间包装起来。否则会冲突

string的构造/析构实现

string的构造方式常用的有三种:

string()、string(const char*str)、

string()的模拟实现

误区:不要这么初始化

测试:

string.cpp

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普

{
	string::string()
		:_str(nullptr), _size(0),_capacity(0)
	{

	}
	string::string(const char* str)
	{

	}
	const char*string:: c_str() const
	{
		return _str; 
	}
}

string.h

cpp 复制代码
#pragma once
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
	//命名空间:不吉波普

{
	class string
	{
	public:
		string();
		string(const char* str);
		//用c_str来打印
		//之所以不先用流插入流提取是因为它们相对比较复杂,后面进行学习
		const char* c_str() const;
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
};

test.cpp

cpp 复制代码
#include<iostream>
using namespace std;
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test1()
	{
		string s1;
		cout<<s1.c_str()<<endl;
	}
};
int main()
{
	Boogiepop::string_test1();
	return 0;
}

经过测试,程序崩溃

为什么会崩溃呢?

因为返回的_str是一个const类型的指针。const char*的指针与其他指针不同,其他指针都会按十六进制的地址去打印,但是const char* 不会。const char*更多指向一个串,对指向的空间上的内容,直到'\0'停止。但_str被初始化为nullptr,而且不能被修改,空指针直接的解引用就会报错。

正确的初始化方式为:

cpp 复制代码
namespace Boogiepop
	//命名空间:不吉波普

{
	string::string()
		:_str(new char[1] {'\0'}), _size(0), _capacity(0)
	{

	}
	string::string(const char* str)
	{

	}
	const char*string:: c_str() const
	{
		return _str; 
	}
}

string(const char* str)的模拟实现

cpp 复制代码
string::string(const char* str)
	:_str(new char[strlen(str) + 1])//strlen是不包含'\0'的长度
	,_size(strlen(str)), _capacity(strlen(str))
{
	strcpy(_str, str);
}

但是这么初始化并不好。因为strlen是在运行时候计算数值,连续调用三个strlen会让程序运行变得很慢。

那么我们是否可以这么改呢?

cpp 复制代码
string::string(const char* str)
	:_size(strlen(str))
	,_str(new char[_size + 1])//strlen是不包含'\0'的长度
	, _capacity(_size)
{
	strcpy(_str, str);
}

也是不可以的。

因为初始化列表是严格按照类中声明的顺序来的,声明的顺序为:

cpp 复制代码
private:
	char* _str;
	size_t _size;
	size_t _capacity;

我们可不可以将_size顺序调换呢?理论上确实可以解决这个问题。但是这样的程序是耦合的。改变的一个就会影响后面的。项目中应当极力避免的。

如果我们要改可以这样

cpp 复制代码
string::string(const char* str)
	:_size(strlen(str))//strlen是不包含'\0'的长度
{
	_str=new char[_size + 1];
	_capacity=_size;
	strcpy(_str, str);
}

构造的合并

我们也可以将两种构造方式合并。无参的构造和带参数的构造是可以合并的------写全缺省的构造函数

但是不能初始化为nullptr,程序会崩溃。可以初始化为'\0'或者

string.h

cpp 复制代码
#pragma once
#include<string.h>//包含C语言的头文件
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
	//命名空间:不吉波普
{
	class string
	{
	public:
		//string的构造函数
		//string();//合并了构造函数
		string(const char* str="");
		//用c_str来打印
		//之所以不先用流插入流提取是因为它们相对比较复杂,后面进行学习
		const char* c_str() const;
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
};

string.cpp

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普

{
	//string的构造
	/*string::string()
		:_str(new char[1] {'\0'}), _size(0), _capacity(0)
	{ 

	}*/
	//string的合并
	string::string(const char* str)
		:_size(strlen(str))//strlen是不包含'\0'的长度
	{
		_str=new char[_size + 1];
		_capacity=_size;
		strcpy(_str, str);
	}
	const char*string:: c_str() const
	{
		return _str; 
	}
}

string的拷贝构造函数

string.h

cpp 复制代码
//拷贝构造函数
string(const string& str);

string.cpp

cpp 复制代码
//拷贝构造函数
string::string(const string& str)
{

}

现代写法:

cpp 复制代码
void string::swap(string& s)
{
	std::swap(_str, s._str);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}
string::string(const string& s)
{
	string tmp(s._str);
	swap(tmp);
}

赋值重载

声明:

cpp 复制代码
//赋值重载
string& operator=(const string& s);
//赋值重载
string& operator=(const string& s);
//赋值重载
string& operator=(const string tmp);

方法一

cpp 复制代码
//赋值重载
//方法一
string& string::operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		memcpy(tmp, s._str, s._size + 1);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}

	return *this;
}

方法二

cpp 复制代码
//方法二
string& string::operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s);
		swap(tmp);
	}

	return *this;
}

方法三

cpp 复制代码
//方法三
string& string::operator=(string tmp)
{
	cout << "string& string::operator=(string tmp)" << endl;

	swap(tmp);

	return *this;
}

string的析构实现

string.cpp

cpp 复制代码
//string的析构
string::~string()
{
	delete[] _str;//用[]是因为中间可能用多个字符
	_str = nullptr;
	_size = 0;
	_capacity = 0;
}

string函数的实现

size()的模拟实现

cpp 复制代码
//size()函数
size_t string::size() const
{
	return _size;
}

operator[]的模拟实现

cpp 复制代码
	char& string::operator[](size_t i)
		//可以写的operator[]()函数
	{
		assert(i< _size);//断言写的是为真的条件,为假会直接报错
		return _str[i];
	}
	const char& string::operator[](size_t i) const
		//只读的operator[]()函数
	{
		assert(i < _size);//断言写的是为真的条件,为假会直接报错
		return _str[i];
	}

测试:

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test1()
	{
		string s1;
		cout<<s1.c_str()<<endl;
		string s2("hello world");
		cout<<s2.c_str()<<endl;
		for (size_t i = 0; i < s2.size(); i++)
		{
			s2[i]++;
			cout<<s2[i]<<" ";
		}
	}
};
int main()
{
	Boogiepop::string_test1();
	return 0;
}

结果为:

运行正常

迭代器模拟实现

正向迭代器

string.h

cpp 复制代码
//迭代器
typedef char* iterator;
//正向迭代器
iterator begin();
iterator end();

string.cpp

cpp 复制代码
//正向迭代器
string::iterator string::begin()
{
	return _str;
}
string::iterator string::end()
{
	return _str + _size;
}

测试:

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test2()
	{
		const string s1("the song of the end world ");
		string::iterator it1 = s1.begin();
		while (it1 != s1.end())
		{
			cout<<*it1<<" ";
			it1++;
		}
		cout<<endl;
	}
};
int main()
{
	//Boogiepop::string_test1();
	Boogiepop::string_test2();
	return 0;
}

这样是不通过的,因为涉及到了权限的放大,所以我们还需要模拟实现const迭代器

const正向迭代器

string.h

cpp 复制代码
//迭代器
typedef char* iterator;
typedef const char* const_iterator;
//正向迭代器
iterator begin();
iterator end();
//正向const迭代器
const_iterator begin() const;
const_iterator end() const;

string.cpp

cpp 复制代码
//正向const迭代器
string::const_iterator string::begin() const
{
	return _str;
}
string::const_iterator string::end() const
{
	return _str + _size;
}

但是const迭代器目前只是简单实现,实际上const迭代器的实现十分复杂。需要后面的知识才能明白

string的插入函数

reserve

cpp 复制代码
void string::reserve(size_t n)
	//扩容函数
{
	if (n > _capacity)
	{
		char* new_str = new char[n + 1];
		strcpy(new_str, _str);
		delete[] _str;
		_str = new_str;
		_capacity = n;
	}
}

pushback

string.cpp

cpp 复制代码
void string::push_back(char c)
	//尾插函数
{
	if (_size >= _capacity)
	{
		size_t new_capacity =_capacity == 0? 4 : _capacity *2;
		reserve(new_capacity);
	}
	_str[_size] = c;
	++_size;
	_str[_size] = '\0';
}

test.cpp

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test3()
	{
		string s1("the song of the end world ");
		cout<<s1.c_str()<<endl;
		s1.push_back('!');
		cout<<s1.c_str()<<endl;
	}
};
int main()
{
	Boogiepop::string_test3();
	return 0;
}

测试成功'

append

cpp 复制代码
void string::append(const char* str)
{
	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		size_t new_capacity = len + _size <_capacity * 2?  _capacity *2: len + _size;
	}
	strcpy(_str + _size, str);
	_size += len;
}

+=运算符重载

string.h

cpp 复制代码
string& operator+=(char ch);
string& operator+=(const char* str);

string.cpp

cpp 复制代码
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}

测试:

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test3()
	{
		string s1("the song of the end world ");
		cout<<s1.c_str()<<endl;
		s1.push_back('!');
		cout<<s1.c_str()<<endl;
		s1.append(" ------ singer");
		cout<<s1.c_str()<<endl;
		s1+= " ------ singer";
		cout<<s1.c_str()<<endl;
		s1+= '~';
		cout<<s1.c_str()<<endl;
	}
};
int main()
{
	Boogiepop::string_test3();
	return 0;
}

结果为:

测试均成功

insert

string.h

cpp 复制代码
//插入函数
void insert(size_t pos, const char* str);
void insert(size_t pos, char ch);

string.cpp

cpp 复制代码
	//插入函数
	void string::insert(size_t pos, char ch)
	{
		assert(pos<=_size);
		if (_size >= _capacity)
		{
			size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(new_capacity);
		}
		//挪动数据
		//方案一:
	/*	int end = _size;
		while (end >=(int) pos)
		{
			_str[end + 1] = _str[end];
			--end;
		}*/
		//方案二:
		size_t end = _size+1;
		while (end > pos)
		{
			_str[end] = _str[end-1];
			--end;
		}
		_str[pos] = ch;
		++_size;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size+len >= _capacity)
		{
			size_t new_capacity =2*_capacity>len+_size? 2*_capacity : len + _size;
			reserve(new_capacity);
		}
		//挪动数据
		size_t end = _size+len;
		while (end >pos+len-1)
		{
			_str[end] = _str[end-len];
			--end;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = _str[i];
		}
	}

测试:

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test5()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		s1.insert(0, "d");
		cout << s1 << endl;
		s1.insert(10, "singer");
		cout << s1 << endl;
	}
};
int main()
{
	Boogiepop::string_test5();
	return 0;
}

测试结果为:

成员变量:npos

string.h

cpp 复制代码
//定义成员变量npos
static const size_t npos = -1;

erase

string.h

cpp 复制代码
//erase函数
string& erase(size_t pos = 0, size_t len = npos);

string.cpp

cpp 复制代码
	//定义成员变量npos
	static const size_t npos = -1;
	//erase函数
	string& string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		// 要删除的数据,大于pos后面的字符个数
		// pos后面全删
		if (len == npos || len >= (_size - pos))
		{
			_size = pos;
			_str[_size] = '\0';
		}
		else
		{
			size_t i = pos + len;
			memmove(_str + pos, _str + i, _size + 1 - i);
			_size -= len;
		}
		return *this;
	}

test.cpp

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test6()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		s1.erase(0, 4);
		cout << s1 << endl;
	}
};
int main()
{
	Boogiepop::string_test6();
	return 0;
}

测试结果正确

pop_back

string.h

cpp 复制代码
//pop_back函数
void pop_back()

string.cpp

cpp 复制代码
//pop_back函数
void string::pop_back()
{
	assert(_size > 0);
	--_size;
	_str[_size] = '\0';
}

test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test6()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		s1.erase(0, 4);
		cout << s1 << endl;
		s1.pop_back();
		s1.pop_back();
		s1.pop_back();
		s1.pop_back();
		s1.pop_back();
		cout << s1 << endl;
	}
};
int main()
{
	Boogiepop::string_test6();
	return 0;
}

测试结果正确

find

string.h

cpp 复制代码
//查找函数
size_t find(char ch, size_t pos = 0);
size_t find(const char *str, size_t pos = 0 );

string.cpp

cpp 复制代码
//查找函数
size_t string::find(char ch, size_t pos )
{
	for (int i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
}
size_t string::find(const char* str, size_t pos )
{
	const char* p = strstr(_str+pos, str);
	if (p == nullptr)
	{
		return npos;
	}
	else
	{
		return p - _str;
	}
}

test.cpp

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test7()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		size_t P=s1.find("the");
		if (P != string::npos)
		{
			cout<<"找到了,位置在:"<<P<<endl;
		}
			
		else
		{
			cout<<"没找到"<<endl;
		}
		size_t U = s1.find("o");
		if (U != string::npos)
		{
			cout << "找到了,位置在: " << U << endl;
		}

		else
		{
			cout << "没找到" << endl;
		}
			
	}
};
int main()
{
	Boogiepop::string_test7();
	return 0;
}

测试成功:

substr

string.h

cpp 复制代码
//substr函数
string substr(size_t pos = 0, size_t len = npos) const;

string.cpp

cpp 复制代码
//substr函数
string string::substr(size_t pos , size_t len ) const
{
	if (len == npos || len > _size - pos)
	{
		len=_size - pos;
	}
	string ret;
	ret.reserve(len);
	for (size_t i = 0;i < len;i++)
	{
		ret+=_str[pos+i];
	}
	return ret;
}

test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test8()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		string s2=s1.substr(0, 4);
		cout << s2 << endl;
		string s3=s1.substr(10, 6);
		cout << s3 << endl;
	}
};
int main()
{
	Boogiepop::string_test8();
	return 0;
}

测试结果:

clear函数

cpp 复制代码
//清除函数
void string::clear()
{
	_size = 0;
	_str[0] = '\0';
}

swap函数

cpp 复制代码
void swap(string& x, string& y)
	{
		x.swap(y);
	}

流插入与流提取

流插入

string.h

cpp 复制代码
//流插入
ostream&  operator<< (ostream& out, const string& str);
//流提取
iostream& operator>> (iostream& in, string& str);

在这里不必要使用友元函数来访问私有,因为访问c_str就足够访问内部的变量了

string.cpp

cpp 复制代码
//流提取
ostream& operator<< (ostream& out, const string& str)
{
	out<<str.c_str();
	return out;
}

但是这么写有一个漏洞

cpp 复制代码
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test4()
	{
		string s1("the song of the end world ");
		cout<<s1<<endl;
		s1 += '\0';
		cout<<s1<<endl;
		s1 += '!';
		cout<<s1<<endl;
	}
};
int main()
{
	Boogiepop::string_test4();
	return 0;
}

结果为:

不仅打印不出来'\0',而且连后面的'!'都无法打印出来了。

因此改变一下为:

cpp 复制代码
//流插入
ostream& operator<< (ostream& out, const string& str)
{
	for (int i = 0; i < str.size(); i++)
	{
		out<<str[i];
	}
	return out;
}

c_str与C语言保持一致,遇到'\0'就停止了

流提取

get()是C++自己的函数,用来从输入流中读取一个字符

string.cpp

cpp 复制代码
//流提取
istream& operator>>(istream& in, string& s)
{
	s.clear();
	char buff[128];
	int i = 0;
	//get()是C++自己的函数,用来从输入流中读取一个字符
	char ch = in.get();
	while (ch != ' ' && ch != '\n')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}

		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

getline函数

string.cpp

cpp 复制代码
istream& getline(istream& in, string& s, char delim)
	{
		s.clear();

		char buff[128];
		int i = 0;

		char ch = in.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}

		return in;
	}

比较函数

声明:

cpp 复制代码
//比较函数
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;

<

cpp 复制代码
bool string::operator<(const string& s) const
{
	size_t i1 = 0, i2 = 0;
	while (i1 < _size && i2 < s._size)
	{
		if (_str[i1] < s._str[i2])
		{
			return true;
		}
		else if (_str[i1] > s._str[i2])
		{
			return false;
		}
		else
		{
			++i1;
			++i2;
		}
	}

<=

cpp 复制代码
bool string::operator<=(const string& s) const
{
	return *this < s || *this == s;
}

>

cpp 复制代码
bool string::operator>(const string& s) const
{
	return !(s <= *this);
}

>=

cpp 复制代码
bool string::operator>=(const string& s) const
{
	return !(*this < s);
}

==

cpp 复制代码
bool string::operator==(const string& s) const
{
	size_t i1 = 0, i2 = 0;
	while (i1 < _size && i2 < s._size)
	{
		if (_str[i1] != s[i2])
		{
			return false;
		}
		else
		{
			++i1;
			++i2;
		}
	}
	return i1 == _size && i2 == s._size;
}

!=

cpp 复制代码
bool string::operator!=(const string& s) const
{
	return !(*this == s);
}

源代码

string.h

cpp 复制代码
#pragma once
#include<iostream> 

#include<string.h>//包含C语言的头文件
#include<assert.h>
using namespace std;
//防止测试的时候与系统的string冲突,故用Boogiepop命名空间封装
namespace Boogiepop
	//命名空间:不吉波普
{
	class string
	{
	public:
		//string的构造函数
		//string();//合并了构造函数
		string(const char* str="");
		//用c_str来打印
		//之所以不先用流插入流提取是因为它们相对比较复杂,后面进行学习
		//c_str()函数
		const char* c_str() const;
		//拷贝构造函数
		string(const string& s);
		//string的析构函数
		~string(); 
		//赋值重载
		string& operator=(const string& s);
		//赋值重载
		string& operator=(const string& s);
		//赋值重载
		string& operator=(const string tmp);
		//size()函数
		size_t size() const;
		//operator[]()函数
		char& operator[](size_t i);//可以写的operator[]()函数
		const char& operator[](size_t i) const;//只读的operator[]()函数
		//迭代器
		typedef char* iterator;
		typedef const char* const_iterator;
		//正向迭代器
		iterator begin();
		iterator end();
		//正向const迭代器
		const_iterator begin() const;
		const_iterator end() const;
		//string插入相关的函数
		void reserve(size_t n);
		void push_back(char c);
		void append(const char* str);
		string& operator+=(char ch);
		string& operator+=(const char* str);
		//插入函数
		void insert(size_t pos, const char* str);
		void insert(size_t pos, char ch);
		//定义成员变量npos
		static const size_t npos=-1 ;
		//erase函数
		string& erase(size_t pos = 0, size_t len = npos);
		//pop_back函数
		void pop_back();
		//查找函数
		size_t find(char ch, size_t pos = 0);
		size_t find(const char *str, size_t pos = 0 );
		//substr函数
		string substr(size_t pos = 0, size_t len = npos) 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;
		bool operator!=(const string& s) const;
		//清除函数
		void clear();
		//swap函数
		void swap(string& s);
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
	//流插入
	ostream&  operator<< (ostream& out, const string& str);
	//流提取
	istream& operator>> (istream& in, string& str);
	//getline函数
	istream& getline(istream& in, string& str, char delim='\n');
};

string.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普

{
	//string的构造
	/*string::string()
		:_str(new char[1] {'\0'}), _size(0), _capacity(0)
	{ 

	}*/
	//string的合并
	string::string(const char* str)
		:_size(strlen(str))//strlen是不包含'\0'的长度
	{
		_str=new char[_size + 1];
		_capacity=_size;
		strcpy(_str, str);
	}
	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	//拷贝构造函数
	/*string::string(const string& s)
	{
		_str = new char[s._size + 1];
		memcpy(_str,s._str, s._size + 1);
		_size = s._size;
		_capacity = s._capacity;
	}*/
	//拷贝构造函数现代写法
	string::string(const string& s)
	{
		string tmp(s._str);
		swap(tmp);
	}
	//string的析构
	string::~string()
	{
		delete[] _str;//用[]是因为中间可能用多个字符
		_str = nullptr;
		_size = 0;
		_capacity = 0;
	}
	//赋值重载
	//方法一
	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			char* tmp = new char[s._capacity + 1];
			memcpy(tmp, s._str, s._size + 1);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
		}

		return *this;
	}
	//方法二
	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			string tmp(s);
			swap(tmp);
		}

		return *this;
	}
	//方法三
	string& string::operator=(string tmp)
	{
		cout << "string& string::operator=(string tmp)" << endl;

		swap(tmp);

		return *this;
	}
	//c_str函数
	const char*string:: c_str() const
	{
		return _str; 
	}
	//size()函数
	size_t string::size() const
	{
		return _size;
	}
	//operator[]()函数
	char& string::operator[](size_t i)
		//可以写的operator[]()函数
	{
		assert(i< _size);//断言写的是为真的条件,为假会直接报错
		return _str[i];
	}
	const char& string::operator[](size_t i) const
		//只读的operator[]()函数
	{
		assert(i < _size);//断言写的是为真的条件,为假会直接报错
		return _str[i];
	}
	//正向迭代器
	string::iterator string::begin()
	{
		return _str;
	}
	string::iterator string::end()
	{
		return _str + _size;
	}
	//正向const迭代器
	string::const_iterator string::begin() const
	{
		return _str;
	}
	string::const_iterator string::end() const
	{
		return _str + _size;
	}
	//string插入相关的函数
	void string::reserve(size_t n)
		//扩容函数
	{
		if (n > _capacity)
		{
			char* new_str = new char[n + 1];
			memcpy(new_str, _str,_size + 1);
			delete[] _str;
			_str = new_str;
			_capacity = n;
		}
	}
	void string::push_back(char c)
		//尾插函数
	{
		if (_size >= _capacity)
		{
			size_t new_capacity =_capacity == 0? 4 : _capacity *2;
			reserve(new_capacity);
		}
		_str[_size] = c;
		++_size;
		_str[_size] = '\0';
	}
	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (len + _size > _capacity)
		{
			size_t new_capacity = len + _size <_capacity * 2?  _capacity *2: len + _size;
		}
		memcpy(_str + _size, str+1,len + 1);
		_size += len;
	}
	string& string::operator+=(char ch)
	{
		push_back(ch);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}
	//插入函数
	void string::insert(size_t pos, char ch)
	{
		assert(pos<=_size);
		if (_size >= _capacity)
		{
			size_t new_capacity = _capacity == 0 ? 4 : _capacity * 2;
			reserve(new_capacity);
		}
		//挪动数据
		//方案一:
	/*	int end = _size;
		while (end >=(int) pos)
		{
			_str[end + 1] = _str[end];
			--end;
		}*/
		//方案二:
		size_t end = _size+1;
		while (end > pos)
		{
			_str[end] = _str[end-1];
			--end;
		}
		_str[pos] = ch;
		++_size;
	}
	void string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (_size+len >= _capacity)
		{
			size_t new_capacity =2*_capacity>len+_size? 2*_capacity : len + _size;
			reserve(new_capacity);
		}
		//挪动数据
		size_t end = _size+len;
		while (end >pos+len-1)
		{
			_str[end] = _str[end-len];
			--end;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = _str[i];
		}
	}
	//定义成员变量npos
	static const size_t npos = -1;
	//erase函数
	string& string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);

		// 要删除的数据,大于pos后面的字符个数
		// pos后面全删
		if (len == npos || len >= (_size - pos))
		{
			_size = pos;
			_str[_size] = '\0';
		}
		else
		{
			size_t i = pos + len;
			memmove(_str + pos, _str + i, _size + 1 - i);
			_size -= len;
		}
		return *this;
	}
	//pop_back函数
	void string::pop_back()
	{
		assert(_size > 0);
		--_size;
		_str[_size] = '\0';
	}
	//查找函数
	size_t string::find(char ch, size_t pos )
	{
		for (int i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
	}
	size_t string::find(const char* str, size_t pos )
	{
		const char* p = strstr(_str+pos, str);
		if (p == nullptr)
		{
			return npos;
		}
		else
		{
			return p - _str;
		}
	}
	//substr函数
	string string::substr(size_t pos , size_t len ) const
	{
		if (len == npos || len > _size - pos)
		{
			len=_size - pos;
		}
		string ret;
		ret.reserve(len);
		for (size_t i = 0;i < len;i++)
		{
			ret+=_str[pos+i];
		}
		return ret;
	}
	//比较函数
	bool string::operator<(const string& s) const
	{
		size_t i1 = 0, i2 = 0;
		while (i1 < _size && i2 < s._size)
		{
			if (_str[i1] < s[i2])
			{
				return true;
			}
			else if (_str[i1] > s[i2])
			{
				return false;
			}
			else
			{
				++i1;
				++i2;
			}
		}
		return i2<s._size;
	}
	bool string::operator<=(const string& s) const
	{
		return *this < s || *this == s;
	}
	bool string::operator>(const string& s) const
	{
		return !(s <= *this);
	}
	bool string::operator>=(const string& s) const
	{
		return !(*this < s);
	}
	bool string::operator==(const string& s) const
	{
		size_t i1 = 0, i2 = 0;
		while (i1 < _size && i2 < s._size)
		{
			if (_str[i1] != s[i2])
			{
				return false;
			}
			else
			{
				++i1;
				++i2;
			}
		}
		return i1 == _size && i2 == s._size;
	}
	bool string::operator!=(const string& s) const
	{
		return !(*this == s);
	}
	//清除函数
	void string::clear()
	{
		_size = 0;
		_str[0] = '\0';
	}
	//swap函数
	void swap(string& x, string& y)
	{
		x.swap(y);
	}
	//流插入
	ostream& operator<< (ostream& out, const string& str)
	{
		for (int i = 0; i < str.size(); i++)
		{
			out << str[i];
		}
		return out;
	}
	//流提取
	istream& operator>>(istream& in, string& s)
	{
		s.clear();
		char buff[128];
		int i = 0;
		//get()是C++自己的函数,用来从输入流中读取一个字符
		char ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}

			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}
	//getline函数
	istream& getline(istream& in, string& str, char delim )
	{
		str.clear();
		char buff[128];
		int i = 0;

		char ch = in.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				str += buff;
				i = 0;
			}

			ch = in.get();
		}

		if (i > 0)
		{
			buff[i] = '\0';
			str += buff;
		}
		return in;
	}
}

test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include"string.h"
namespace Boogiepop
	//命名空间:不吉波普
	//三个文件的命名空间被认为是同一个命名空间
{
	void string_test1()
	{
		string s1;
		cout<<s1.c_str()<<endl;
		string s2("hello world");
		cout<<s2.c_str()<<endl;
		for (size_t i = 0; i < s2.size(); i++)
		{
			s2[i]++;
			cout<<s2[i]<<" ";
		}
	}
	void string_test2()
	{
		const string s1("the song of the end world ");
		string::const_iterator it1 = s1.begin();
		while (it1 != s1.end())
		{
			cout<<*it1<<" ";
			it1++;
		}
		cout<<endl;
	}
	void string_test3()
	{
		string s1("the song of the end world ");
		cout<<s1.c_str()<<endl;
		s1.push_back('!');
		cout<<s1.c_str()<<endl;
		s1.append(" ------ singer");
		cout<<s1.c_str()<<endl;
		s1+= " ------ singer";
		cout<<s1.c_str()<<endl;
		s1+= '~';
		cout<<s1.c_str()<<endl;
	}
	void string_test4()
	{
		string s1("the song of the end world ");
		cout<<s1<<endl;
		s1 += '\0';
		cout<<s1<<endl;
		s1 += '!';
		cout<<s1<<endl;
		cout<<s1.c_str()<<endl;
		s1 += " ------ singer";
		cout << s1 << endl;
	}
	void string_test5()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		s1.insert(0, "d");
		cout << s1 << endl;
		s1.insert(10, "singer");
		cout << s1 << endl;
	}
	void string_test6()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		s1.erase(0, 4);
		cout << s1 << endl;
		s1.pop_back();
		s1.pop_back();
		s1.pop_back();
		s1.pop_back();
		s1.pop_back();
		cout << s1 << endl;
	}
	void string_test7()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		size_t P=s1.find("the");
		if (P != string::npos)
		{
			cout<<"找到了,位置在:"<<P<<endl;
		}
			
		else
		{
			cout<<"没找到"<<endl;
		}
		size_t U = s1.find("o");
		if (U != string::npos)
		{
			cout << "找到了,位置在: " << U << endl;
		}

		else
		{
			cout << "没找到" << endl;
		}
	}
	void string_test8()
	{
		string s1("the song of the end world ");
		cout << s1 << endl;
		string s2=s1.substr(0, 4);
		cout << s2 << endl;
		string s3=s1.substr(10, 6);
		cout << s3 << endl;
	}
	void string_test9()
	{
		string s1("the song of the end world "), s2("the song of the end world");
		string s3("the song of the end world"), s4("the song of the end worldX");
		string s5("the song of the end worldC "), s6("the song of the end world");
		cout << (s1 < s2) << endl;
		cout << (s3 < s4) << endl;
		cout << (s5 < s6) << endl;
	}
	void string_test10()
	{
		string s1("the song of the end world "), s2("the story of the end world");
		cout << s1 << s2 << endl;
		cin>>s1>>s2;
		cout << s1 << s2 << endl;
	}
};
int main()
{
	//Boogiepop::string_test1();
	//Boogiepop::string_test2();
	//Boogiepop::string_test3();
	//Boogiepop::string_test4();
	//Boogiepop::string_test5();
	//Boogiepop::string_test6();
	//Boogiepop::string_test7();
	//Boogiepop::string_test8();
	//Boogiepop::string_test9();
	Boogiepop::string_test10();
	return 0;
}

本篇博客到此结束,希望通过string的模拟实现能对string有更深刻的理解。

下一篇我们将继续学习STL中vector的内容进行学习

在这里求一个点赞,谢谢

封面图自取:

相关推荐
reduceanxiety15 分钟前
机试 | vector/array Minimum Glutton C++
数据结构·c++·算法
2301_794461571 小时前
力扣-最大连续一的个数
数据结构·算法·leetcode
MonKingWD1 小时前
【redis原理篇】底层数据结构
数据结构·数据库·redis
小黄人软件1 小时前
OpenSSL 与 C++ 搭建一个支持 TLS 1.3 的服务器
服务器·开发语言·c++
blog_wanghao2 小时前
MFC: 文件加解密(单元测试模块)
c++·单元测试·mfc
清心歌2 小时前
二叉树遍历
数据结构·算法
武昌库里写JAVA2 小时前
Vue3编译器:静态提升原理
java·开发语言·spring boot·学习·课程设计
日晞2 小时前
深浅拷贝?
开发语言·前端·javascript
大模型铲屎官3 小时前
【深度学习-Day 16】梯度下降法 - 如何让模型自动变聪明?
开发语言·人工智能·pytorch·python·深度学习·llm·梯度下降
不二狗3 小时前
每日算法 -【Swift 算法】字符串转整数算法题详解:myAtoi 实现与正则表达式对比
算法·正则表达式·swift