模拟实现string类

 

6eb591ffbffb6b573f3a17197328474c.jpeg

 

 string类有很多的成员函数,这里我来梳理一下常用的几个,并模拟实现一下可以凑合用的my_string,话不多说直接开干:

注意事项

为了和库里的string冲突,所以就将自己实现的my_string放在一个命名空间里

namespace cr  

成员变量

 

private:
		char* _arr;
		size_t _size;
		size_t _capacity;
public:
		const static size_t npos;

和往常一样,static修饰的成员变量要单独定义,毕竟该成员属于整个类

const size_t cr::my_string::npos = -1;

构造函数 

 

my_string(const char* arr = "");
cr::my_string::my_string(const char* arr)
	:_size(strlen(arr))
	,_capacity(_size)
{
	_arr = new char[_capacity+1];//加一个\0
	strcpy(_arr, arr);
}

为了避免无惨初始化的情况,这里是要给个缺省值的,空字符串也是有一个元素的('\0'),这里的字符指针_arr是不走初始化列表的,因为我们声明时的顺序会影响着初始化列表执行的顺序,所以最好单独初始化。而string类最容易出错的地方就是末尾的'\0',所以在开辟空间时要多开一个存放'\0'

拷贝构造函数

 

void cr::my_string::swap(my_string& s)
{
	std::swap(_arr, s._arr);
	std::swap(_size, s._size);
	std::swap(_capacity, s._capacity);
}
cr::my_string::my_string(const my_string& tmp)//拷贝构造函数
	:_arr(nullptr)
	,_size(0) 
	,_capacity(0)
{
	my_string s(tmp._arr);//this并没有初始化,没有处理
	swap(s);
}

拷贝构造的形参一定不要忘记引用接收,防止陷入死循环(因为在调用拷贝构造是就已经反生了一次传参,自定义类型数据传参又会调用拷贝构造函数)。这里的拷贝构造函数是有点不一样的,这里调用了构造函数

my_string s(tmp._arr);//tmp对象就是需要拷贝的内容

仅仅通过一个构造函数就实现了深拷贝,后续的swap会将从tmp对象拷贝好的对象s和this进行交换,但是this里的数据并没有进行初始化,所以交换就可能出问题,所以在这之前在初始化列表中将this初始化一下。所以这里相对于平常的拷贝构造就可以少些很多代码(实际交给函数执行了)

赋值运算符 

 

cr::my_string& cr::my_string::operator=(my_string tmp)
{
	swap(tmp);
	return *this;
}

这里是不是更加不可思议了呢,实际上就是调用了拷贝构造函数而已,传参的时候,tmp就是通过拷贝构造得到的数据,再swap将得到的数据换给this达到目的,所以就完成赋值操作,结束后而且还自动帮你释放tmp这个临时“中间人”的内存。

 

operator[]

char& operator[](size_t i);
const char& operator[](size_t i) const;
char& cr::my_string::operator[](size_t i)
{
	assert(i < _size);
	return _arr[i];
}
const char& cr::my_string::operator[](size_t i) const
{
	assert(i < _size);
	return _arr[i];
}

 这里库中实现的就是断言,不仅仅是在string中,STL也是断言判断的。而且最好是实现成const和非const两种,一个只可读,一个可读可改,保障了安全性。

迭代器 

typedef char* iterator;
typedef const char* const_iterator;

iterator begin();
iterator end();
const_iterator begin()const;
const_iterator end()const;
cr::my_string::iterator cr::my_string::begin()
{
	return _arr;
}

cr::my_string::iterator cr::my_string::end()
{
	return &_arr[_size];//_arr+_size
}

cr::my_string::const_iterator cr::my_string::begin()const
{
	return _arr;
}

cr::my_string::const_iterator cr::my_string::end()const
{
	return &_arr[_size];//_arr+_size
}

这里要注意的是end()函数返回的值可不是指向最后一个有效数据,指向的是最后一个有效数据的下一个(即'\0')同样是实现了两种。

 reserve与resize

这两个成员函数的功能一定要区分开。

void cr::my_string::reserve(size_t n)
{
	if(n>_capacity)//防止外面调用该函数时出错
	{
		_capacity = n;
		char* tmp = new char[_capacity + 1];
		strcpy(tmp, _arr);
		delete[]_arr;
		_arr = tmp;
	}
}

 reserve的功能就是开空间,一般的使用场景是在提前已经知道数据空间大小的情况,改变的是_capacity的值(不会改变_size的值)。如果原数据的_capacity比你reserve的_capacity还要大时,是不会进行缩容的,所以reserve就是扩容的作用。

void cr::my_string::resize(int n,char p)
{
	if (n > _size)
	{
		reserve(n);
		for (int i = size(); i < n; i++)
		{
			_arr[i] = p;
		}
		_arr[n] = 0;
	}
	else
	{
		_arr[n] = 0;
	}
	_size = n;//改变_size的值
}

其实resize的第二个参数是有缺省值‘\0’的,resize的功能也是对数据空间的操作,resize就像它的命名一样,改变的是_size成员变量的值,当resize的空间小于原_size时就是缩数据,将多的数据都扔掉(容量不会变),那么如果resize的空间大于原_size时,就是扩容,如果没有第二个参数的话,多的空间会给个缺省值‘\0’,否则就给传过来的值。


所以记住一点,reserve无法改变size的值,而resize可以改变size的值。

 就说这么多了,其他的几个常见的成员函数的源码就放在下面了:

源码


void cr::my_string::push_back(char p)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}

	_arr[_size] = p;
	_size++;
	_arr[_size] = 0;
}

void cr::my_string::append(const char* p)
{
	int len = strlen(p);
	reserve(len + _capacity);

	strcat(_arr, p);//该函数会将\0拷贝进去
	_size += len;
	
}

void cr::my_string::operator+=(const char* p)
{
	append(p);
}
void cr::my_string::operator+=(char p)
{
	push_back(p);
}
void cr::my_string::operator+=(cr::my_string& s)
{
	*this += s._arr;//s自定义类型,执行结束会调用析构函数
}

void cr::my_string::insert(size_t pos, char p)
{
	assert(pos <= _size);
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : _capacity * 2);
	}
	size_t end = _size + 1;
	while (end > pos)
	{
		_arr[end] = _arr[end-1];
		end--;
	}
	_arr[pos] = p;
	_size++;
}
void cr::my_string::insert(size_t pos,const char* p)
{
	assert(pos <= _size);
	int len = strlen(p);
	if (_size+len>_capacity)
	{
		reserve(_capacity+len);
	}
	size_t end = _size + len;
	while (end >= pos + len)
	{
		_arr[end] = _arr[end - len];
		end--;
	}
	for (int i = 0; i < len; i++)
	{
		_arr[pos++] = p[i];
	}
	_size += len;

}

void cr::my_string::erase(size_t pos, size_t len)
{
	if (len == npos || len + pos >= size())
	{
		_arr[pos] = 0;
		_size = pos;
		return;
	}
	for (int i = pos + len; i <= size(); i++)//i最后指向\0
	{
		_arr[pos++] = _arr[i];
	}
	_size -= len;

}

bool cr::my_string::operator<(const my_string& s)const
{
	return  strcmp(this->_arr, s._arr) < 0;
}
bool cr::my_string::operator>(const my_string& s)const
{
	return strcmp(this->_arr, s._arr) > 0;
}
bool cr::my_string::operator==(const my_string& s)const
{
	return !(*this < s) && !(*this > s);
}
bool cr::my_string::operator>=(const my_string& s)const
{
	return *this > s || *this == s;
}
bool cr::my_string::operator<=(const my_string& s)const
{
	return *this < s || *this == s;
}
bool cr::my_string::operator!=(const my_string& s)const
{
	return !(*this == s);
}

ostream& cr::operator<<(ostream& out, const my_string& s)
{
	//for(int i=0;i<s.size();i++)
	//	out << s[i];

	for (auto ch : s)
	{
		out << ch;
	}

	return out;
}

void cr::my_string::clear()
{
	_arr[0] = 0;
	_size = 0;
}

istream& cr::operator>>(istream& in, my_string& s)
{	
    //in>>会以空格和换行进行分割,不会读入
	s.clear();
	char tmp[100]; int i = 0;
	char ch;
	ch = in.get();//会读取每一个字符
	while (ch != ' ' && ch != '\n')
	{
		if(i<99)
		{
			tmp[i++] = ch;
		}
		else
		{
			tmp[i++] = 0;
			s += tmp;//可以存起来再插入,少扩几次
			i = 0;
		}
		ch = in.get();
	}
	tmp[i++] = 0;
	s += tmp;

	return in;
}


size_t cr::my_string::find( char p,int pos)
{
	assert(pos < _size);
	for (int i = pos; i < _size; i++)
	{
		if (_arr[i] == p)
			return i;
	}
	return npos;
}
size_t cr::my_string::find(const char* p,int pos)
{
	assert(pos < _size);
	char* q = strstr(_arr + pos, p);
	if (q)
	{
		return q - _arr;
	}
	return npos;
	
}

cr::my_string cr::my_string::substr(size_t pos, size_t len)
{
	my_string s;
	int n;
	if (len == npos || len + pos >= _size)
		n = _size;
	else
		n = len + pos;
	for (int i = pos; i < n; i++)
	{
		s += _arr[i];
	}
	return s;
}

cr::my_string::~my_string()
{
	delete[]_arr;
	_arr = nullptr;
	_size = _capacity = 0;
}

 

3f5c025fe1962cf0afe02df73e31e233.jpeg

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高居沉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值