string类的模拟实现

1、创建命名空间

创建自己的命名空间以免到时候和库里的string冲突

namespace xxx
{
    //string类
    class string
    {
        //...
    };
}

2、确定框架以及成员变量

string类它首先得是个类,那么我们就可以用到类的实例化

正常情况下string类需要以下几个成员变量

namespace test
{
	class string
	{
		//成员函数
	public:

		//成员变量
	private:
		char* _str;
		size_t _size;
		size_t _capacity;

		const static size_t npos = -1;
	};
}

前三个好理解,分别是指向字符数组首元素的指针、实际大小以及容量

那这个npos是什么意思呢?

npos是一个常数,可以用来充当某些函数的返回值

也可参考库的string类对其的解释

string类的模拟实现_第1张图片

 3、成员函数

确定完框架以及创建好成员变量之后我们就要对类的成员函数下手了。当然我这只是简单的实现一些常用的成员函数,毕竟库里string的成员函数这么多,全部实现也太肝了

1.构造函数和析构函数

首先是构造函数

库里面有这么多

string类的模拟实现_第2张图片

实现一下前俩个

(1)默认构造

给缺省值的原因是初始化string类的时候不能让其为空值,当然'\0'和""都行 

string(const char* str = "")
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

 (2)拷贝构造

拷贝构造的不同之处就是在于它是基于同一个类所创建的不同对象而构造的对象

所以要拷贝所传对象的字符串、容量和实际大小

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

析构函数

~string()
{
	delete[] _str;
	_str = nullptr;
	_size = 0;
	_capacity = 0;
}

2.c_str

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

3.operator=(赋值运算符重载)和operator[]

普通的运算符。然不适用于类,所以我们要重载一下,让string类也可以用

思路和前面的拷贝构造差不多

同样是基于传过来的对象创建一个对象,也有一些区别

赋值可以是对象以及存在,只是把传过来对象拷贝到需要赋值的那个对象里去

那么就有一个前提:这个对象存在且和传过来的对象不相等(当然这个!=也要重载,后面会说)

存在且相等那还赋个毛的值啊(bushi)

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

也可以用swap搞(string的)

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

既然是数组就可以用[]去访问

所以我们也要搞一个

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

3.swap

既然讲到了swap那就顺便也把swap的string版本搞一下

调库里的嘎嘎拷就完事了

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

4.iterator(迭代器) 

typedef char* iterator;
typedef const char* const_iterator;


iterator begin()
{
	return _str;
}
const_iterator begin()const
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

const_iterator end()const
{
	return _str + _size;
}

5. clear和empty

clear是让字符串变为空字符串

改下size然后加个\0就行了

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

 empty判空

bool empty()const
{
	if (_size == 0)
	{
		return true;
	}
	return false;
}

 6.size和capacity

直接return好吧

size_t size()const
{
	return _size;
}

size_t capacity()const
{
	return _capacity;
}

7.push_back(尾插)和reserve(扩容)

尾插嘛,自然是要先判断要不要扩容了(实际大小等于容量就该扩liao)

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

判断完就直接插了,尾插吗扩不扩都要插的嘛

至于这个扩容,扩多少要先说嘛(参数n),你不说我很难为你办事啊

n一定要大于容量,你n小于或等于这个容量,我怎么扩,哪有越扩越小的道理

我这写的是异地扩(原地扩很麻烦搞不了一点)

开个空间拷一拷,哦对了,那个+1是留给\0的

拷完把之前的空间删删,让指针指向刚开好的空间

然后容量改为n就行了

void reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}
}

8.append和operator+=

push_back都来了那怎么少的了append(尾插字符串)

思路和push_back一样

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

就是实现需要注意它拷的是字符串,尤其是在长度和位置这两方面

+=它来啦(直接调append)

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

9.resize 

调整字符串大小

将字符串大小调整为 n 个字符的长度
如果 n 大于当前字符串长度,则通过在末尾插入任意数量的字符来扩展当前内容,以达到 n 的大小。如果指定了 c,则新元素将初始化为 c 的副本,否则,它们是值初始化的字符。

如果 n 小于当前字符串长度,则当前值将缩短为其前 n 字符,并删除第 n 个字符以外的字符。

void resize(size_t n, char c = '\0')
{
	if (n >= _size)
	{
		if (n > _capacity)
		{
			reserve(n);
		}
		while (_size < n)
		{
			_str[_size] = c;
			_size++;
		}
		_str[_size] = '\0';
	}
	else
	{
		_size = n;
		_str[_size] = '\0';
	}
}

10.insert和erase

insert

在pos插入一个字符c

pos铁定要小于等于_size滴

然后就判断要不要扩容

往pos位置插就要挪一下pos+1位置到后面的字符

往pos位置放c,然后_size++

string& insert(size_t pos, char c)
{
	assert(pos <= _size);
	if (_size == _capacity)
	{
		size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newCapacity);
	}
	size_t end = _size + 1;
	while (end > pos)
	{
	    _str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = c;
	_size++;
	return *this;
}

插字符串也差不多

只是需要注意一下长度

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

erase

要告诉我在哪(pos)删,删多少(len)

npos缺省值,如果没给默认从pos位置全删

如果给了,那要分两种情况:全删或者删一点

全删是len超出_size时或者pos+len超出_size

不然就是删一点,strcpy覆盖就完事啦

注意_size减去删掉的len长度

string& erase(size_t pos, size_t len = npos)
{
	assert(pos < _size);
    if (len == npos || pos + len >= _size)
	{
		_str[pos] = '\0';
		_size = pos;
	}
	else
	{
	    strcpy(_str + pos, _str + pos + len);
		_size -= len;
	}
	return *this;
}

11.find

从pos位置开始找c,pos缺省值是0

for循环遍历嘛,如果i位置的字符等于c的话就返回i

找不到就返回npos

size_t find(char c, size_t pos = 0) const
{
	for (size_t i = pos; i < _size; i++)
	{
	    if (_str[i] == c)
		{
			return i;
		}
	}
	return npos;
}

12.substr(截取子字符串)

从pos位置开始取,取len个字符

pos要小于等于_size

要看长度来分是否要从pos位置截取字串

创建对象str

扩和字串一样大的容

将母串pos开始len个字符一点点拷到str里

返回str就行了

string substr(size_t pos = 0, size_t len = npos)
{
	assert(pos <= _size);
	size_t end = pos + len;
	if (len == npos || pos + len > _size)
	{
		end = _size;
	}
	string str;
	str.reserve(end - pos);
	for (size_t i = pos; i < end; i++)
	{
		str += _str[i];
	}
	return str;
}

你可能感兴趣的:(开发语言,c++)