string的模拟实现

1.string的框架

在string当中需要定义三个变量。

分别是str,size,capacity。

str是储存指向开辟的动态内存的地址。

size是存储现有的数据个数。

capacity是用来存储动态开辟的内存的大小。

2.构造和析构函数的实现

string需要在动态内存当中申请空间,所以必须要用自己写的构造与析构函数来申请和释放动态内存,用默认自带的会有内存泄露的风险。

(1).构造函数
string(const char* str = "")
	:_size(strlen(str))
	,_capacity(_size)

{
	_str = new char[_capacity + 1];
	strcpy(_str, str);
	
}

思路:

先将数组的数据个数和大小存储好,然后根据数据大小来开辟动态内存的空间将地址赋值给str,然后将数组拷贝到开辟的动态内存当中。

(2).析构函数
~string() {
	delete[] _str;
	_str = nullptr;
	_capacity = _size = 0;
}

思路:

析构函数就只需要将指向的动态内存空间释放掉就可以了,但是这里为了规整,还是把str置空,size和capacity赋值为0.

(3).拷贝构造函数
string(const string& str) 
:_str(nullptr)
	,_size(0)
	,_capacity(0)
{
	string tmp(str._str);
	swap(tmp);
}

拷贝狗仔函数首先要进行列表的初始化,因为内置类型编译器一般不会进行初始化,会给一个随机值,可能会导致一些不必要的错误。

思路:利用构造函数基于用来拷贝的string类对象 str 来创建出一个临时的string类tmp,tmp可以视为str的拷贝。然后将tmp与被拷贝构造的string类对象 this 交换,这样一来this就成为了str的拷贝,tmp存储的是原来this所存储的数据,之后在出拷贝构造函数的作用域时,tmp会进行析构,把原来属于this的数据析构掉,这样一来拷贝构造就完成了。

这里还需要用到另一个函数swap,这边就一并列出来

	void swap(string& s) {
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

思路:

直接将两个string类当中存储的三个数据str,size,capacity进行交换,可以用库函数当中的swap来实现。

3.基础函数的实现

先来实现一些string当中较为常用的功能。

(1).size()
size_t size()const {
	return _size;
}

返回数据个数。

(2).capacity()
size_t capacity()const {
	return _capacity;
}

返回动态内存大小

(3).operator[]
	char& operator[](size_t pos) {
		assert(pos < _size);
		return _str[pos];
	}
const char& operator[](size_t pos)const {
	assert(pos < _size);
	return _str[pos];
}

下标访问函数的实现分为可修改和不可修改。


(4).reserve(int n) 
	void reserve(int n) {
		if (n > _capacity) {
			char* newnode = new char[n+1];
			strcpy(newnode, _str);
			delete[] _str;
			_str = newnode;
			_capacity = n;
		}
	}

reserve可以提前开辟想要的动态内存空间,可以很好的减少扩容的次数。

思路:

首先检查需要的大小是否大于原来的空间,大于则直接在动态内存当中开辟想要的大小,然后将原本存储的数据拷贝到新的动态内存当中,将原本的动态内存释放,str的地址修改为新的动态内存地址,然后修改size和capacity的大小。


    (5).resize(int n, char ch = '\0')
void resize(int n, char ch = '\0') {
	if (n <= _size) {
		_str[n] = '\0';
		_size = n;
	}
	else {
		reserve(n);
		while (_size < n) {
			_str[_size] = ch;
			_size++;
		}
		_str[_size] = '\0';
	}
}

resize可以减少和增加动态内存当中存储的数据个数,如果内存不足可以开辟更多的动态内存,但是不能释放内存,因为原则上动态内存不能只释放一部分。

思路:首先判断需要的数据个数n,小于或等于则直接将字符串第n个位置改为\0,并修改size的大小为n。如果大于,则需要面对两种情况,需要的内存大于现有的动态内存和不大于。但是这里可以忽视掉,直接使用reserve,因为reserve当中有关于大小的检查,只有大于时才会发生扩容,之后从下标size开始直到n-1都修改为字符ch,将下标为n修改为\0,如果没有传字符过来,默认ch为\0。

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

尾部插入数据

思路:

先查看内存是否足够,不足则用reserve进行扩容,之后将下标为size的数据修改为n,++size,添加\0。

(7).append(const char* arr)

sting当中尾部插入数据,字符和字符串并没有用函数重载,而是使用了两个不同的函数,因为是模拟实现,所以也是用两个函数来实现尾部插入。

void append(const char* arr) {
	int len = strlen(arr);
	if (_size + len > _capacity) {
		reserve(_size + len);
	}
	strcpy(_str + _size, arr);
	_size += len;
}

思路:

先取出arr字符串的长度,判断内存是否足够,不足则扩容,之后从原字符串末尾开始将arr拷贝进去,修改size。

(8).测试

将上面实现的函数都测试一下是否正确。

void test_string1() {
	string s1("hello world");
	cout << s1.c_str() << endl;
	for (int i = 0; i < s1.size(); i++) {
		cout << s1[i];
	}
	cout << endl;
	string s2(s1);
	cout << s2.c_str() << endl;
	s1.push_back(' ');
	s1.append("hello");
	cout << s1.c_str() << endl;
}

在这里为了方便查看字符串还需要实现一个函数c_str用来提取字符串

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

运行

没有问题。

4.运算符的重载以及迭代器

   (1).operator+=(char n) 
	string& operator+=(char n) {
		push_back(n);
		return *this;
	}
	

思路:

直接复用push_back就可以,然后返回+=后的值。

(2).operator+=(const char* arr)
	string& operator+=(const char* arr) {
		append(arr);
		return *this;
	}

直接复用append就可以,然后返回+=后的值。

(3).operator<(const string& s)const
	bool operator<(const string& s)const {
		return strcmp(_str, s._str)<0;
	}

思路:

直接复用标准库里的strcmp就可以,并判断返回值。

(4).operator==(const string& s)const
	bool operator==(const string& s)const {
		return strcmp(_str, s._str) == 0; 
	}

思路:

直接复用标准库里的strcmp就可以,并判断返回值。

(5).operator<=(const string& s)const
bool operator<=(const string& s)const {
	return *this<s||*this==s;
}

思路:

复用小于和等于,只要有一个为真便返回真。

(6). operator>(const string& s)const
	bool operator>(const string& s)const {
		return !(*this <= s);
	}

思路:

复用小于等于并取反。

(7).operator>=(const string& s)const
bool operator>=(const string& s)const {
	return !(*this < s);
}

思路:

复用小于并取反。

(8).operator!=(const string& s)const 
bool operator!=(const string& s)const {
	return !(*this == s);
}

思路:

复用等于并取反

(9).operator=( string s) 

string& operator=( string s) {
	if (this != &s) {
		swap(s);
	}
	return *this;
}

思路:

先判断是否为同一个数,不是同一个则复用swap交换,这边使用的时传值调用,所以交换不会影响到原本的数,并且出了这个函数会将换过来的this的数据进行析构。

(9).迭代器的实现
typedef char* iterator;
typedef const char* const_iterator;
iterator begin() {
	return _str;
}
iterator end() {
	return _str + _size;
}
const_iterator begin()const {
	return _str;
}
const_iterator end()const {
	return _str + _size;
}

所有的迭代器都要命名为iterator,并定义begin和end函数来表示第一个数据的指针和最后一个数据的指针,以此来完成迭代。在string当中迭代器就是指针,但在其他类当中不一定是,到时候就需要运算符重载等方法来实现迭代器的遍历。

(9).clear()

用来清除所有数据,但不会释放空间

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

思路:

将字符串的首个字符修改为\0,并且修改size为0.

(10).operator<<(ostream& out,  string& s)
ostream& operator<<(ostream& out,  string& s) {
	for (auto e : s) {
		out << e;
	}
	return out;
}

思路:

将字符依次取出来投在屏幕上,最后返回流插入

(11).operator>>( istream& in,  string& s)
istream& operator>>( istream& in,  string& s) {
	s.clear();
	size_t i = 0;
	char buff[129];
	char ch;
	ch = in.get();
		while (ch != ' ' && ch != '\n') {
			buff[i++] = ch;
			if (i == 128) {
				buff[i] = '\0';
			s += buff;
			i = 0;
			}
			ch = in.get();
		}
		if (i != 0) {
			buff[i] = '\0';
			s += buff;
		}
	return in;
}

思路:

先使用clear清除原本的数据,然后一个个提取到ch上,如果遇到空格或\n就截止,但正常的流提取无法提取空格和\n,也就是提取不到结束的条件。所以需要用到一个函数,get(),这是一个库函数,可以让流提取能够取到空格和\n。但同时还有另一个问题,扩容太过频繁,所以需要用到一个缓冲区,先建立一个数组,将提取到的字符先保存的数组当中,当数组满了或者遇到结束条件时再进行插入,可以有效的减少扩容的次数。

(12).测试

void test_string2() {
	string s1 = "hello world";
	string s2 = "hello world";
	cout << (s1 == s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 > s2) << endl;
	s1[0] = 'z';
	cout << (s1 == s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 > s2) << endl;
	for (auto e: s1) {
		cout << e;
	}
	cout << endl;
	s1 += '*';
	s1 += "########";
	cout << s1 << endl;
}

将实现的所有函数进行一下测试

运行

5.所有代码

#include <iostream>
#include<assert.h>
using namespace std;
#pragma once
namespace world {
class string {
public:
	typedef char* iterator;
	typedef const char* const_iterator;
	iterator begin() {
		return _str;
	}
	iterator end() {
		return _str + _size;
	}
	const_iterator begin()const {
		return _str;
	}
	const_iterator end()const {
		return _str + _size;
	}
	string(const char* str = "")
		:_size(strlen(str))
		,_capacity(_size)

	{
		_str = new char[_capacity + 1];
		strcpy(_str, str);
		
	}
	string(const string& str) 
	:_str(nullptr)
		,_size(0)
		,_capacity(0)
	{
		string tmp(str._str);
		swap(tmp);
	}
	~string() {
		delete[] _str;
		_str = nullptr;
		_capacity = _size = 0;
	}
	void swap(string& s) {
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}
	size_t size()const {
		return _size;
	}
	size_t capacity()const {
		return _capacity;
	}
	const char* c_str()const {
		return _str;
	}
	char& operator[](size_t pos) {
		assert(pos < _size);
		return _str[pos];
	}
	const char& operator[](size_t pos)const {
		assert(pos < _size);
		return _str[pos];
	}

	void reserve(int n) {
		if (n > _capacity) {
			char* newnode = new char[n+1];
			strcpy(newnode, _str);
			delete[] _str;
			_str = newnode;
			_capacity = n;
		}
	}
	void resize(int n, char ch = '\0') {
		if (n <= _size) {
			_str[n] = '\0';
			_size = n;
		}
		else {
			reserve(n);
			while (_size < n) {
				_str[_size] = ch;
				_size++;
			}
			_str[_size] = '\0';
		}
	}
	void push_back(char n) {
		if (_size == _capacity) {
			reserve(_capacity == 0 ? 4 : _capacity * 2);
		}
		_str[_size] = n;
		_size++;
		_str[_size] = '\0';
	}
	void append(const char* arr) {
		int len = strlen(arr);
		if (_size + len > _capacity) {
			reserve(_size + len);
		}
		strcpy(_str + _size, arr);
		_size += len;
	}
	string& operator+=(char n) {
		push_back(n);
		return *this;
	}
	string& operator+=(const char* arr) {
		append(arr);
		return *this;
	}
	bool operator<(const string& s)const {
		return strcmp(_str, s._str)<0;
	}
	bool operator==(const string& s)const {
		return strcmp(_str, s._str) == 0; 
	}
	bool operator<=(const string& s)const {
		return *this<s||*this==s;
	}
	bool operator>(const string& s)const {
		return !(*this <= s);
	}
	bool operator>=(const string& s)const {
		return !(*this < s);
	}
	bool operator!=(const string& s)const {
		return !(*this == s);
	}
	string& operator=( string s) {
		if (this != &s) {
			swap(s);
		}
		return *this;
	}
	void clear() {
		_str[0] = '\0';
		_size = 0;
	}
	
private:
	char* _str;
	int _size;
	int _capacity;
};
ostream& operator<<(ostream& out,  string& s) {
	for (auto e : s) {
		out << e;
	}
	return out;
}

istream& operator>>( istream& in,  string& s) {
	s.clear();
	size_t i = 0;
	char buff[129];
	char ch;
	ch = in.get();
		while (ch != ' ' && ch != '\n') {
			buff[i++] = ch;
			if (i == 128) {
				buff[i] = '\0';
			s += buff;
			i = 0;
			}
			ch = in.get();
		}
		if (i != 0) {
			buff[i] = '\0';
			s += buff;
		}
	return in;
}
void test_string1() {
	string s1("hello world");
	cout << s1.c_str() << endl;
	for (int i = 0; i < s1.size(); i++) {
		cout << s1[i];
	}
	cout << endl;
	string s2(s1);
	cout << s2.c_str() << endl;
	s1.push_back(' ');
	s1.append("hello");
	cout << s1.c_str() << endl;
}

void test_string2() {
	string s1 = "hello world";
	string s2 = "hello world";
	cout << (s1 == s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 > s2) << endl;
	s1[0] = 'z';
	cout << (s1 == s2) << endl;
	cout << (s1 != s2) << endl;
	cout << (s1 >= s2) << endl;
	cout << (s1 <= s2) << endl;
	cout << (s1 < s2) << endl;
	cout << (s1 > s2) << endl;
	for (auto e: s1) {
		cout << e;
	}
	cout << endl;
	s1 += '*';
	s1 += "########";
	cout << s1 << endl;
}

  • 11
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值