C++---string模拟实现

前面我们已经对string进行了简单的介绍,只要会用各个函数即可,下面是string的模拟实现。
在这里我们可以定义一个自己的命名空间,防止与库里的string造成冲突。

namespace haifan
{
	class string
	{
	public:
		.....
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}

构造函数和析构函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhE4Trrp-1690193767195)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230724170813742.png)]

这是库里的几种构造函数,在这里,我只实现了一种。

如果在传参的时候我们是以haifan::string a的形式或者是以 haifan::string b("123") 我们可以考虑用 string(const char* str = "") 这样的形式来写构造函数,当没有参数的时候,就用str的缺省值,有参数就用传递的参数。

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

析构函数很简单,把_str释放掉,在置为空指针即可

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

begin和end

begin和end分别由两种写法,一种是带const的,一种是不带的。

typedef char* iterator;
typedef const char* const_iterator;

const_iterator begin() const
{
	return _str;
}

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

iterator begin()
{
	return _str;
}

iterator end()
{
	return _str + _size;
}

reserve和resize

reserve是为字符串预留空间的一个函数,并且只有一个参数,表示要为字符串预留多少空间,如果n要比capacity小,可以不做处理,反之,要为字符串预留n + 1的空间,因为要放一个\0

void reserve(size_t n)
{
    if (_capacity < n)
    {
        //注意还有一个\0
        char* tmp = new char[n + 1];

        memcpy(tmp, _str, _size + 1);
        delete[] _str;
        _str = tmp;
        _capacity = n;
    }
}

resize是用字符c来填充多出的元素空间。在改变元素个数时,如果是增多,可能会改变底层容量的大小,如果减少,底层空间总大小不变。

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

        for (int i = _size; i < n; i++)
        {
        _str[i] = c;
        }
        _str[n] = '\0';
        _size = n;
    }
}

push_back和append

push_back是在字符串的尾部,添加一个字符—尾插,但在进行插入的时候,要注意空间是否足够,对此进行一个检查即可。

在插入字符后,别忘了在将\0进行一次尾插

void push_back(char ch)
{
    if (_size + 1 > _capacity)
    {
    	if (_capacity == 0)
        {
         	reserve(4);
        }
        else
        	reserve(2 * _capacity);
        }

    _str[_size++] = ch;

    _str[_size] = '\0';
}

append是将一个字符串进行尾插,同样,要先进行空间检查。如果空间不足,将空间开辟到跟尾插后的字符串一样的大小即可。

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

    memcpy(_str + _size, str, len + 1);

    _size += len;

}

c_str

c_str是将string类以C字符串的形式返回。

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

empty,size,capacity,clear

empty用于判断string是否为空。

bool empty() const
{
	return _capacity == 0;
}

size用于判断string的长度

size_t size() const
{
    return _size;
}

capacity用于判断string当前的容量

size_t capacity() const
{
    return _capacity;
}

clear用于清空string,但是capacity并不会被清0

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

拷贝构造和赋值

拷贝构造设计到了空间的开辟,要用深拷贝,浅拷贝可能会造成两个string共用同一块地址。

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

赋值在这里,只实现了两种情况,一种是 string a = b 另一种是 string a = "abcd",前者利用拷贝构造先创建一个临时变量,然后用swap将*this和tmp进行交换,从而实现=的目的。后者是开辟了一块空间,将str里的内容,拷贝到tmp里面,然后将_str=tmp。

		void swap(string& str)
		{
			std::swap(_str, str._str);
			std::swap(_size, str._size);
			std::swap(_capacity, str._capacity);
		}
		
		string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				swap(tmp);
				return *this;
			}
		}

		string& operator= (const char* str)
		{
			char* tmp = new char[strlen(str) + 1];
			memcpy(tmp, str, strlen(str) + 1);
			_size = strlen(str);
			_capacity = strlen(str) + 1;
			delete[] _str;
			_str = tmp;
			return *this;
		}

+=和比较大小

加等于一个字符,其实就是尾插

		string& operator+= (char c)
		{
			push_back(c);

			return *this;
		}

加等于一个字符串其实就是append

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

			return *this;
		}

用于比较的代码很好写,只要写出来一个,其他的都可以复用。

比如小于

两种情况 两个string的长度相同或者不相同,如果相同直接memcpy进行字符串的比较即可,如果不相同,看哪个string短,先比短的部分,如果短的部分都相同,则string长的大,如果短的部分不相同,在比较的时候做出判断即可。

		bool operator<(const string& s)
		{
			if (s._size == _size)
			{
				return memcpy(_str, s._str, _size);
			}
			else
			{
				if (_size > s._size)
				{
					for (int i = 0; i < s._size; i++)
					{
						if (_str[i] > s._str[i])
						{
							return false;
						}
					}

					return false;
				}
				else
				{
					for (int i = 0; i < _size; i++)
					{
						if (_str[i] > s._str[i])
						{
							return false;
						}
					}
					return true;
				}

			}

			
			//int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
			//return ret == 0 ? _size < s._size : ret < 0;
		}
		一个好理解的写法,一个两行的写法

写出来一个,其他的都可以以<为基础来写

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

			return false;
		}

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

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

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

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

[]重载

string类可以像字符串一样,用下表来访问,这是因为把[]进行了重载

		char& operator[] (size_t pos) 
		{
			assert(pos < _size);

			return _str[pos];
		}

		const char& operator[] (size_t pos) const
		{
			assert(pos < _size);

			return _str[pos];
		}

insert和erase

在第pos位置上插入一个字符,只需要把pos和pos后面的数据向后移动一位即可。

	void insert(size_t pos,char c)
		{

			assert(pos < _size);

			if (_size + 1 > _capacity)
			{
				reserve(2 * _capacity);
			}

			size_t tail = _size;
			size_t head = pos;
			
			while (tail >= pos && tail != npos)
			{
				_str[tail] = _str[tail - 1];
				tail--;
			}
			_str[pos - 1] = c;
			++_size;
			_str[_size] = '\0';
		}	

在pos位置上插入一个字符串,将pos和pos之后的数据,向后移动字符串的长度即可。

	void insert(size_t pos, const char* str)
		{
			assert(pos < _size);

			const size_t len = strlen(str);

			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			size_t tail = _size;

			while (tail >= pos && tail != npos)
			{
				_str[tail + len] = _str[tail];
				tail--;
			}
			for (int i = 0; i < len; i++)
			{
				_str[i + pos] = str[i];
			}
			_size += len;

			_str[_size] = '\0';
		}

erase(size_t pos = 0, size_t len = npos)

pos是要删除的起始位置,len是要删除多少个,不写默认把pos之后的元素全部删除。

npos是 size_t npos = -1,因为size_t是无符号数,-1是一个正数且是一个及其大的值

		void erase(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			if (len + pos > _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t tail = pos + len;
				while (tail < _size)
				{
					_str[pos++] = _str[tail++];
				}
				_size -= len;
				_str[_size] = '\0';
			}
		}

find查找

size_t find(char ch, size_t pos = 0)

从pos位置开始查找某个字符,如果找到了,返回下标,没找到,返回npos

		size_t find(char ch, size_t pos = 0)
		{
			assert(pos < _size);

			for (size_t i = pos; i < _size; i++)
			{
				if (_str[i] == ch)
				{
					return i;
				}
			}

			return npos;
		}

size_t find(const char* str, size_t pos = 0)

查找子串,如果找到了,返回第一次找到子串的位置,反之,返回npos

		size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < _size);

			const char* ptr = strstr(_str + pos, str);
			if (ptr)
			{
				return ptr - _str;
			}
			else
			{
				return npos;
			}
		}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值