C++:从零开始,万字模拟实现string(超详细)

前言:因为模拟实现string中途修修改改了很多代码,所以建议大家先复制总代码在自己的vs下,然后再来一步一步看

目录

第一步:将"hello world"赋值给自定义的string

 第二步:打印出hello world在屏幕上

第三步:用下标可以修改字符串的任意字符

第四步:通过下标遍历string的每个字符

第五步:浅拷贝和深拷贝字符串

第六步:=运算符的重载

 第七步:实现增删查改——修改基础private

第八步:增删查改——添加字符和字符串(resize和reserve,push_back和append)

第十步:三种遍历方式 

第十一步:增删查改——指定插入和指定删除(insert和erase的实现)

 第十二步:实现增删查改——查找find函数实现

总代码:

 string.h:

test.cpp: 


------------------------开始------------------------

为了模拟string,我们需要自己命名一个新的空间,我这里是’bit’,以方便和库中的string作区分

然后创建两个,一个是头文件’string.h’,另一个是.cpp文件"test.cpp"

 

第一步:将"hello world"赋值给自定义的string

我这里自己定义了一个空间名字为”bit”,然后定义了一个String类。对于我们定义的新字符串_str,

我们需要定义出他的大小,因为原本的字符串中有’\0’,所以在进行字符串拷贝的时候,我们需要在原本的空间大小上+1,然后利用strcpy进行拷贝。

最后再加入一个析构函数

namespace bit
	{
		class String
		{
		public:
			String(const char* str)//因为有’\0’,所以需要在原有的基础上+1个空间
				:_str(new char[strlen(str) + 1])//比如'hello world',实际上是'hello world\0'
			{
				strcpy(_str, str);//拷贝字符串
			}
			~String()//析构函数,将资源还给内存
			{
				if (_str)
				{
					delete[] _str;
				}
			}
		private:
			char* _str;//自定义一个自己的新指针,指向一个只有'\0'的字符串空间
		};
}

将上述代码放入string.h中,然后我们在test.cpp调用以下代码进行测试

#include<iostream>
#include"string.h"
using namespace std;//上面是头文件,下面才是代码
void test_string1()
{
	bit::String s1("hello world");
}
int main()
{
	test_string1();
	return 0;
}

在监视窗口中我们可以看到,我们已经将hello world赋予了,我们自己创建的string的s1中

 第二步:打印出hello world在屏幕上

因为我们还没有重载流插入和流提取的运算符,所以如果我们要输出hello world,这里可以使用一个函数c_str.

在string.h的public中加入这段代码

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

然后就可以在test.app中使用了

void test_string1()
{
	bit::String s1("hello world");
	cout << s1.c_str() << endl;//加入这句话
}

然后我们在屏幕上就可以看到这段话

注:如果不了解c_str的小伙伴可以看看我的这篇文章

C++:什么是c_str_幻荼的博客-CSDN博客


 

第三步:用下标可以修改字符串的任意字符

 对于修改字符,我们就需要对[]进行运算符重载

在string.h的public放入

char& operator[](size_t pos)
		{
			assert(pos < strlen(_str));//记得在前面加头文件
			return _str[pos];
		}

然后在test.cpp中修改字符

void test_string1()
{
	bit::String s1("hello world");
	cout << s1.c_str() << endl;
	s1[0] = 'X';//加入这句话
	cout << s1.c_str() << endl;//加入这句话
}

我们可以看到hello world的第一个字符h被替换成了X。 

 备注:如果对运算符重载有问题的小伙伴可以观看我的这篇文章C++:运算符重载简介_幻荼的博客-CSDN博客

第四步:通过下标遍历string的每个字符

一共是有三种方式:

1.下标+[]

2.迭代器

3. 范围for

因为是从零开始模拟,我们这里先讲解第一种,下标+[]。

我们首先在string.h的public中加入size函数,以便求得下标的整体范围.

size_t size()
		{
			return strlen(_str);
		}

有了这个函数,我们就能再test中遍历每个字符了

void test_string1()
{
	bit::String s1("hello world");
	cout << s1.c_str() << endl;
	s1[0] = 'X';
	cout << s1.c_str() << endl;
	for (size_t i = 0; i < s1.size(); i++)//加入这段话
	{
		cout << s1[i] << ' ';
	}
	cout << endl;//加入这段话
}

会有以下结果

我们可以看到我们就遍历出了每个字符。 

备注:如果对strlen不了解的小伙伴可以看我写的这篇文章

strlen简介,和sizeof的区别_幻荼的博客-CSDN博客

第五步:浅拷贝和深拷贝字符串

我们以前使用string的时候,喜欢把一个字符串直接赋值给另一个字符串,即

string s1("123456");
string s2(s1);

那么如何模拟实现呢?

我们先看看直接在test.cpp中试试能不能做到

void test_string2()//这里为了演示深浅拷贝,我重新创立了一个test_string2,前面是1,请将这段代码全部打到test.cpp中
{
	bit::String s1("hello world");
	bit::String s2(s1);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
}

 然后就会出现这个结果:

 

 结果就是,拷贝是拷贝过去了,但是编译器会报错,这是为什么呢?

这个就和我们的深浅拷贝有关了

简单来说就是p2通过浅拷贝,和p1指向了同一个空间

由于栈区的规则是先进后出,当执行完拷贝构造函数的时候,我们程序就会先执行p2的析构函数,导致释放堆区开辟的数据,然后这个已经释放的内存又要再被p1释放,这样就会报错

就是说同一块内存数据被释放了两次,这是编译器所不允许的

 

 所以我们需要进行深拷贝。

深拷贝简单来说就是开一个和p1一样大的空间,然后将p1的数据存放到新空间,然后自己指向新空间

 这样解析的时候,就各自解析各自的了。

为了实现深拷贝,我们只需要加入这段代码:

String(const String& s)这个隐藏了一个this指针,实际上是s2(s1)
			:_str(new char[strlen(s._str) + 1])//开辟(new)一个新空间和s1一样大,来存放数据,+1是因为有’\0’
		{
			strcpy(_str, s._str);//拷贝数据
		}

备注:不了解深浅拷贝的小伙伴可以看看我的这篇文章

C++:浅拷贝和深拷贝简析_幻荼的博客-CSDN博客

第六步:=运算符的重载

为了实现’+=’'+''-'各种运算符的重载,我们首先来实现最简单的’=’符号,即赋值。

我们设定一个场景

void test_string3()//这里是运算符重载,所以重新开了一个test3
{
	bit::String s1("hello world");
	bit::String s2(s1);
	bit::String s3 ("ni hao");
    s1 = s3;//通过’=’,将s3赋值给s1
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
	cout << s3.c_str() << endl;
}

有些同学可能会说,直接用strcpy将s3复制给s1不就行了吗? 

但是对于赋值,我们有三种情况

s1空间<s3空间

s1空间=s3空间

s1空间>s3空间

如果我们直接用strcpy复制,s1空间<s3空间的情况下,s1会造成越界

列如:

 如果这么多个1通过strcpy,s1的空间是肯定放不下的,就会造成越界访问。

那么如果s1空间>s3空间是不是可以了呢?

答案是:可以,但是会造成浪费

列如:

 如果我S1开了一个1000个字节的空间,结果只用来存放s3这样一个字符’1’,那么是不是会造成很严重的浪费呢?

所以说为了避免越界访问和内存浪费,我们就要使得s1的空间大小和s3一模一样。

所以我们进行三步

第一步:删除s1原有内存

第二步:创建一个和s3一样大的内存

第三步:复制数据

这段代码加入到string.h的public中

String& operator=(const String& s)//这里有隐藏的this指针s3(s1)
		{
        if(this!=&s)//防止有人自己给自己赋值,列如s1=s1,这样会导致乱码
           {
			delete[]_str;//删除s1空间
			_str = new char[strlen(s._str) + 1];//创建和s3一样大的空间
			strcpy(_str, s._str);//复制数据
			return *this;//返回数据
           }
		}

然后运行结果如下

就可以看到s1被s3赋值了 

 第七步:实现增删查改——修改基础private

通过前面六步,实际上一个最基础的string已经完成了,为了丰富我们的string,我们就要实现增删查改的功能。

为了实现这些功能,一个_str肯定已经不够了,我们需要size和capacity,即容量和已存储数据的大小

private:
		char* _str;//自定义一个自己的新字符串接受
		size_t _size;//容量(新加入)
		size_t _capacity;//目前大小(新加入)
	};

然后加入新的capacity函数

size_t capacity()const
		{
			return _capacity;
		}

同时我们一系列的函数就需要调整

构造函数

String(const char* str="")//这里全缺省,加入一个空字符串,空字符串中有'\0',防止默认构造出错
			:_size(strlen(str))//新加入
			,_capacity(_size)//新加入
		{
			_str = new char[_capacity + 1];//将开空间调到这里
			strcpy(_str, str);
		}

因为编译器是根据private的顺序进行定义,我们这里_str的初始化使用了capacity,而capacity在privat的最后面,所以为了方便,我们就直接把他在里面初始化了。

然后是 拷贝

String(const String& s)
			:_size(strlen(s._str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}

最后是’=’的重载

String& operator=(const String& s)
		{
			if (this != &s)
			{
				delete[]_str;
				_str = new char[s._capacity + 1];
				strcpy(_str, s._str);
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

这些修改实际上就是增加了我们新加入的size和capacity,然后把原先用strlen求有效字符串长度用capacity替换。(capacity是有效字符串个数,不包括'\0')

第八步:增删查改——添加字符和字符串(resize和reserve,push_back和append)

push_back(添加字符)append(添加字符串)

push_back:

添加字符实际上很简单,只需要考虑扩容,如果容量够直接在末尾添加一个字符,再补一个'\0'即可

void push_back(char ch)
		{
			if (_size == _capacity)//查看是否容量已满
			{
				char* tmp = new char[_capacity * 2 + 1];//new一个新的大空间,+1是因为有'\0'
				strcpy(tmp, _str);//内容复制给新空间
				delete[]_str;//删除旧空间
				_str = tmp;//将旧空间指向新空间
				_capacity *= 2;//'\0'不是有效字符所以*2就可以了
			}
			_str[_size] = ch;//尾插
			++_size;
			_str[_size] = '\0';//最后补一个'\0'
		}

如下图所示: 

 然后我们就可以测试一下

void test_string5()//这里是5了
{
	bit::String s1("hello world");
	s1.push_back('1');
	s1.push_back('X');
	cout << s1.c_str() << endl;
}

可以看到1和X就被尾插了。 

然后接下来是append:

append和push_back几乎一模一样,都是扩容+尾插,却别在于append的扩容不太好直接扩二倍。

假如

string s("123");
s.append("12345678910");

 如果我们只是单纯的扩容2倍,那么可以看到s*2的capacity=6,也不够10个。

所以在这里我们需要计算一共有多少个字符,再根据字符数量来扩容

void append(const char* str)
		{
			size_t len = _size + strlen(str);//原本字符数+需要添加的字符数
			if (len > _capacity)
			{
				reserve(len);//扩容
			}
			strcpy(_str + _size, str);//拷贝数值
			_size = len;//拷贝空间
		}

小伙伴们可能发现了, 我们这里的扩容运用了一个函数reserve(),而没有像之前push_back一样写一串,这是因为我把扩容,重新用reserve包装了一下

其实和上面扩容一模一样

	void reserve(size_t n)
		{
			if (n > _capacity)//如果需要扩容
			{
				char* tmp = new char[n + 1];//创建一个和n一样大的空间防止浪费和越界访问
				strcpy(tmp, _str);//复制数据
				delete[]_str;//删除旧空间
				_str = tmp;//指向新空间
				_capacity = n;//把n给capacity
			}
		}

然后我们也可以吧reserve给push_back使用一下

void push_back(char ch)
		{
			if (_size == _capacity)
			{
				reserve(reserve(_capacity == 0 ? 4 : _capacity * 2););//这里扩容二倍刚好合适,因为是只插入单个字符
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';
		}

在一些特殊的情况下,我们也会使用resize对数据进行插入和初始化。

列如:

string s1("123456");
s1.resize(10,'9')

 这样打印出来的结果是"1234569999".

这里就模拟实现一下resize

void resize(size_t n,char ch='\0')
		{
			if (n < _size)//n<size进行缩容
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				if (n > _capacity)//capacity不够进行扩容
				{
					reserve(n);
				}
				for (size_t i = _size; i < n; i++)//对空余进行初始化操作
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}

至于为什么用reserve,以及什么是resize,不明白的小伙伴可以看我这篇文章

c++:reserve和resize简介和区别_幻荼的博客-CSDN博客

但是————我们平时都不这么用

我们平时插入一个字符串或者字符直接用"+="

例如

string s1("123");
s1+='X';
s1+="686";

s1就成为了"123X686";

所以说,这里我们需要对+=进行运算符重载

因为我们已经实现了单个字符的插入和字符串的插入,所以套过去就好

String& operator+=(const char* str)//字符串的插入,记得隐藏的this指针
		{
			append(str);
			return *this;
		}
		String& operator+=(char ch)//字符的插入,记得隐藏的this指针
		{
			push_back(ch);
			return *this;
		}

然后我们就可以在test.cpp中测试看看

void test_string5()//这里是用来测试插入字符和字符串的test5
{
	bit::String s1("hello world");
	s1.push_back('1');
	s1.push_back('X');
	s1.append("www");
	s1 += 'y';
	s1 += "abc";
	cout << s1.c_str() << endl;
}

结果如下

 第九步:各类运算符重载

有不懂运算符的小伙伴,我在前面已经放了链接,可以自行查看,这里因为太简单所以直接贴上代码

bool operator<(const String& s1, const String& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
	bool operator==(const String& s1, const String& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	bool operator<=(const String& s1, const String& s2)
	{
		return s1<s2||s1==s2;
	}
	bool operator>(const String& s1, const String& s2)
	{
		return !(s1<=s2);
	}
	bool operator>=(const String& s1, const String& s2)
	{
		return !(s1 < s2);
	}
	bool operator!=(const String& s1, const String& s2)
	{
		return !(s1 == s2);
	}

如果不知道什么是strcmp的小伙伴可以看我的这篇文章

c:strcmp函数()比较两个字符串是否相等_幻荼的博客-CSDN博客_如何比较两个字符串内容是否一样

第十步:三种遍历方式 

这一段我自己了解的还不是很深刻,只能先讲解我知道的部分,等后面学习了再来补充吧...

我们已经在最开始利用了下标+[]的方式遍历了每个字符

void test_string6()//这里是专门用来测试三种遍历的test6
{
	bit::String s1("hello world");
	for (size_t i; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
}

这里我们使用第二种方式迭代器

对于迭代器,我们首先需要begin和end两个自定义函数

typedef char* iterator;
		iterator begin()//指向首地址
		{
			return _str;
		}
		iterator end()//指向末尾元素的下一个元素
		{
			return _str + _size;
		}

然后就可以和指针一样遍历整个字符串

bit::String::iterator it = s1.begin();//将字符串第一个字符给it
	while (it != s1.end())//it不为最后一个字符,就继续循环
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

实际上就是这么个情况

第三种方式范围for(不会,等会面学了再来补充)

for (auto& ch : s1)//实际上底层被替换成了迭代器,这里ch实际上是s1的拷贝
	{
       //ch-=1;大家可以试着把这段话加上看看,因为这里是引用传参所以可以改变,如果只是auto ch,那么这段代码就没有作用
		cout << ch << " ";
	}
	cout << endl;

第十一步:增删查改——指定插入和指定删除(insert和erase的实现)

不明白这两个函数是什么的小伙伴可以看看我的这篇文章

c++:insert函数和erase函数的简介_幻荼的博客-CSDN博客

我们刚才已经实现了push_back,和append,以及+=,那么我们算是把尾部的增加给搞定了,但是很多情况下,我们是需要在数据的中间进行增删查改的,所以就需要我们实现指定位置的插入和删除。

insert(指定位置的插入):

对于指定位置的插入,我们一般是插入字符和字符串两种情况。

我们先来实现插入字符

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;
			}
			_str[pos] = ch;
			_size++;
			return *this;
		}

实际上是这么个情况,end指向字符串结尾'\0'的下一个位置,然后依次挪动数据,等到pos和end相等的时候,先判断是否扩容,如果不用扩容就直接在pos这个位置插入这个数据,然后size++

 接下来是字符串:

String& insert(size_t pos,const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (len == 0)
			{
				return* this;
			}
			if (_size + len >= _capacity)//扩容
			{
				reserve(_size + len);
			}
			size_t end = _size + len;
			while (end >pos+len-1 )
			{
				_str[end] = _str[end-len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
		}

实际上是这么个情况:假如我要插入"XXX",的字符在S[0]处,那么我的end就是目前的size+"XXX"字符串的长度,只要当我们pos+”XXX”字符串的长度-1(有"\0")=end,那么我们就停止,然后在pos处用strncpy()函数插入这个字符串

 好,我们下面来进行调试

void test_string7()//这里是用来调试insert和erase的test7
{
	bit::String s("hello world");
	s.insert(1, 'x');
	s.insert(0, "XXX");
	cout << s.c_str() << endl;

}

结果如下:

下面我们来实现指定位置删除erase:

String& earse(size_t pos, size_t len)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)//pos+len>=size
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else//pos+len<size
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					begin++;
				}
				_size -= len;
			}
             return *this;
		}
private:
		char* _str;//自定义一个自己的新指针,指向一个只有'\0'的字符串空间
		size_t _size;
		size_t _capacity;
		const static size_t npos;//新加入,因为库里面也是有无符号的npos
	};

意思如下:

假如,我有一个字符串hello world,我从W(pos)向后删除len个字符,有三种情况

pos+len<size

pos+len=size

pos+len>size

一旦我的pos+len>=size,我们就可以直接在s[pos]处赋值一个'\0',然后把size变为pos,因为字符串遇到'\0'就终止

如果pos+len<size,就拿下面举例,我从W开始向后删除len=3个字符,那么首先我记录一下删除后的第一个字符,就是s[pos+len]处的字符,然后将s[pos+len]处的值赋值给s[pos]处,然后这个值++直到=size。

就是w=l,然后o=d,然后r='\0',然后此时=size,赋值结束。然后把size-=len,就完成了。

 第十二步:实现增删查改——查找find函数实现

这一步很简单,没有什么需要讲解的

size_t find(char ch, size_t pos = 0)
		{
			for (; pos < _size; pos++)//遍历字符串
			{
				if (_str[pos] == ch)//如果等于就返回该字符
				{
					return pos;
				}
			}
			return npos;
		}
		size_t find(const char* str, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, str);//用strstr函数进行字符串查找
              if (p == nullptr)
			{
				return npos;
			}
			  else
			{
				return p - _str;
			}
		}

总代码:

 string.h:

#pragma once
#include<assert.h>
#include<iostream>
using namespace std;
namespace bit
	{
	class String
		{
	public:
		typedef char* iterator;
		typedef char* const const_iterator;
		const_iterator begin()const
		{
			return _str;
		}
		const_iterator end()const
		{
			return _str + _size;
		}
		iterator begin()
		{
			return _str;
		}
		iterator end()
		{
			return _str + _size;
		}
		String()
			:_size(0)
			,_capacity(0)
		{
			_str = new char[1];
			_str[0] = '\0';
		}
		String(const char* str)//
			:_size(strlen(str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, str);
		}
		String(const String& s)
			:_size(strlen(s._str))
			,_capacity(_size)
		{
			_str = new char[_capacity + 1];
			strcpy(_str, s._str);
		}
		~String()//析构函数,将资源还给内存
		{
			if (_str)
			{
				delete[] _str;
				_str = nullptr;
				_size = _capacity = 0;
			}
		}
		String& operator=(const String& s)
		{
			if (this != &s)
			{
				delete[]_str;
				_str = new char[s._capacity + 1];
				strcpy(_str, s._str);
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}
		const char* c_str()const
		{
			return _str;
		}
		char& operator[](size_t pos)
		{
			assert(pos < strlen(_str));
			return _str[pos];
		}
		const char& operator[](size_t pos)const
		{
			assert(pos < strlen(_str));
			return _str[pos];
		}
		void push_back(char ch)
		{
			/*if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size] = ch;
			++_size;
			_str[_size] = '\0';*/
			insert(_size, ch);
		}
		void resize(size_t n,char ch='\0')
		{
			if (n < _size)
			{
				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				if (n > _capacity)
				{
					reserve(n);
				}
				for (size_t i = _size; i < n; i++)
				{
					_str[i] = ch;
				}
				_size = n;
				_str[_size] = '\0';
			}
		}
		String& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
		String& operator+=(char ch)
		{
			push_back(ch);
			return *this;
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* tmp = new char[n + 1];
				strcpy(tmp, _str);
				delete[]_str;
				_str = tmp;
				_capacity = n;
			}
		}
		void append(const char* str)
		{
			/*size_t len = _size + strlen(str);
			if (len > _capacity)
			{
				reserve(len);
			}
			strcpy(_str + _size, str);
			_size = len;*/
			insert(_size, str);
		}
		size_t size()const
		{
			return strlen(_str);
		}
		size_t capacity()const
		{
			return _capacity;
		}
		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;
			}
			_str[pos] = ch;
			_size++;
			return *this;
		}
		String& insert(size_t pos,const char* str)
		{
			assert(pos <= _size);
			size_t len = strlen(str);
			if (len == 0)
			{
				return* this;
			}
			if (_size + len >= _capacity)
			{
				reserve(_size + len);
			}
			size_t end = _size + len;
			while (end >pos+len-1 )
			{
				_str[end] = _str[end-len];
				--end;
			}
			strncpy(_str + pos, str, len);
			_size += len;
			return *this;
		}
		String& earse(size_t pos, size_t len)
		{
			assert(pos < _size);
			if (len == npos || pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t begin = pos + len;
				while (begin <= _size)
				{
					_str[begin - len] = _str[begin];
					begin++;
				}
				_size -= len;
			}
			return *this;
		}
		size_t find(char ch, size_t pos = 0)
		{
			for (; pos < _size; pos++)
			{
				if (_str[pos] == ch)
				{
					return pos;
				}
			}
			return npos;
		}
		size_t find(const char* str, size_t pos = 0)
		{
			const char* p = strstr(_str + pos, str);
			if (p == nullptr)
			{
				return npos;
			}
			else
			{
				return p - _str;
			}
		}
	private:
		char* _str;//自定义一个自己的新指针,指向一个只有'\0'的字符串空间
		size_t _size;
		size_t _capacity;
		const static size_t npos;
	};
	const size_t String::npos = -1;
	ostream& operator<<(ostream& out, const String& s)
	{
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}
	istream& operator>>(istream& in, String& s)
	{
		char ch;
		ch = in.get();
		while (ch != ' ' && ch != '\n')
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}
	bool operator<(const String& s1, const String& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) < 0;
	}
	bool operator==(const String& s1, const String& s2)
	{
		return strcmp(s1.c_str(), s2.c_str()) == 0;
	}
	bool operator<=(const String& s1, const String& s2)
	{
		return s1<s2||s1==s2;
	}
	bool operator>(const String& s1, const String& s2)
	{
		return !(s1<=s2);
	}
	bool operator>=(const String& s1, const String& s2)
	{
		return !(s1 < s2);
	}
	bool operator!=(const String& s1, const String& s2)
	{
		return !(s1 == s2);
	}
}

test.cpp: 

#define _CRT_SECURE_NO_WARNINGS 1
using namespace std;
#include"string.h"
void test_string1()
{
	bit::String s1("hello world");
	cout << s1.c_str() << endl;
	s1[0] = 'X';
	cout << s1.c_str() << endl;
	for (size_t i = 0; i < s1.size(); i++)
	{
		cout << s1[i] << ' ';
	}
	cout << endl;
}
void test_string2()
{
	bit::String s1("hello world");
	bit::String s2(s1);
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
}
void test_string3()
{
	bit::String s1("hello world");
	bit::String s2(s1);
	bit::String s3("ni hao");
	s1 = s3;
	cout << s1.c_str() << endl;
	cout << s2.c_str() << endl;
	cout << s3.c_str() << endl;
}
void test_string4()
{
	bit::String s1("hello world");
	cout << s1.c_str() << endl;
}
void test_string5()
{
	bit::String s1("hello world");
	s1.push_back('1');
	s1.push_back('X');
	s1.append("www");
	s1 += 'y';
	s1 += "abc";
	cout << s1.c_str() << endl;
}
void test_string6()//这里是专门用来测试三种遍历的test6
{
	//1.下标+[]
	bit::String s1("hello world");
	for (size_t i=0; i < s1.size(); i++)
	{
		cout << s1[i] << " ";
	}
	cout << endl;
	//2.迭代器
	bit::String::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	//3.范围for
	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;
}

void test_string7()//这里是用来调试insert和erase的test7
{
	bit::String s("hello world");
	s.insert(1, 'x');
	s.insert(0, "XXX");
	cout << s.c_str() << endl;

}
void test_string9()
{
	bit::String s("hello world");
	cout << s << endl;
}
int main()
{
	try
	{
		test_string9();
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}
	return 0;
}

--------------------------完结----------------------------

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值