【c++】string类 (二) 模拟实现

基础结构

string依然是一个线性的结构,所以说在实现的时候和以前实现的线性结构差不了多少,总体还是我们的老三样------_str,_size和_capacity。

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <assert.h>

namespace yu
{
	class String
	{
	public:

	private:
		char* _str;
		size_t _capacity;//注意_capacity不包含'\0'
		size_t _size;
	};
}

本次实现string的时候,我们把一些简单的小函数在.h中直接定义,所以我们要注意链接错误,.h文件中的函数,如果定义在类里面,默认是内敛的,不会放到符号表,不用担心链接错误,但是如果是在类外面定义的函数,.h文件在test.cpp和string.cpp中都包含了一份,就会有两个这个函数,导致链接错误。

解决方案,将函数设置内敛或者是静态函数,不会放到符号表,只在当前文件可见。当然最简单的办法依旧是做声明与定义分离。

string类的基础成员函数

构造函数

不带参的构造函数
cpp 复制代码
String()
	:_str('\0')
	,_capacity(0)
	,_size(0)
{

}
带参的构造函数
cpp 复制代码
String(const char* str)
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}
缺省参数构造函数

当然我们也可以把这两个糅合一下,使用缺省参数来写一个构造函数

cpp 复制代码
String(const char* str = "")//注意这里不要输入空格,空语句自带'\0'
{
	_size = strlen(str);
	_capacity = _size;
	_str = new char[_capacity + 1];
	strcpy(_str, str);
}

析构函数

cpp 复制代码
	~String()
	{
		delete[] _str;
		_str = nullptr;//释放之后置为空指针预防野指针
		_capacity = _size = 0;

	}

拷贝构造函数

对于指向资源的类类型对象我们必须自己实现拷贝构造函数来实现深拷贝,默认生成的拷贝构造函数是值拷贝,详情见(【c++】类和对象 (中) (类的默认成员函数)-CSDN博客

cpp 复制代码
String(const String &s)
{
	_str = new char[s._capacity + 1];
	strcpy(_str, s._str);
	_size = s._size;
	_capacity = s._capacity;
}

当然还有一些其他的写法,我们可以把交换的功能封装到一个函数,然后将值赋到临时变量之后再整体操作

cpp 复制代码
	void Swap(String& s)
	{
		swap(_capacity,s._capacity);
		swap(_str, s._str);
		swap(_size, s._size);

	}
	String(const String& s)
	{
		String tmp(s._str);
		Swap(tmp);
	}

基础函数

cpp 复制代码
//用这个返回字符串
const char* c_str()  const
{
	return _str;
}


size_t size() const
{
	return _size;
}


size_t capacity() const
{
	return _capacity;
}

运算符重载

[]重载

重载[]之后,就像数组一样访问string。

cpp 复制代码
char& operator[](size_t pos)
{
	assert(pos <= _size);
	char& n = _str[pos];
	return n;
}
const char& operator[](size_t pos) const
{
	assert(pos <= _size);
	char& n = _str[pos];
	return n;
}
赋值运算符重载
cpp 复制代码
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;
}

迭代器

cpp 复制代码
typedef char* iterator;

iterator begin()
{
	return _str;
}
iterator end()
{
	return _str + _size;
}

我们来测试一下

cpp 复制代码
void test01()
{
	String s1("hello !");
	String s2(s1);

	String::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it ;
		it++;
	}
	cout << endl;
	for (auto i: s1)
	{
		cout << i;
	}
	cout << endl;
}

范围for的底层就是迭代器

string的修改器

reserve()

cpp 复制代码
     
	void String::reserve(size_t n)
	{

		
		if (n > _capacity)
		{
			char *str = new char[n+1];
			strcpy(str, _str);
			delete[] _str;  //释放原来的空间
			_str = str;
			_capacity = n;
		}
	}

push_back()

cpp 复制代码
void String::push_back(char ch)
{
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : 2 * _capacity);
	}
	_str[_size] = ch;
	_size++;
	_str[_size] = '\0';//不加这一行没有\0,不会停下来
}

append()

cpp 复制代码
void String::append(const char* str)
{
	size_t len = strlen(str);
	if (_size + len >_capacity)
	{
		if (_size + len > 2 * _capacity)
		{
			reserve(_size + len + 1);
		}
		if (_size + len <= 2 * _capacity)
		{
			reserve(2 * _capacity);
		}
	}
	strcpy(_str+_size, str);
	_size += len;
}

+=运算符重载

单个字符

cpp 复制代码
String& String::operator+=(char ch)
{
	push_back(ch);
	return *this;
}

字符串

cpp 复制代码
String& String::operator+=(const char* str)
{
	append(str);
	return *this;

}

insert()

单个字符

cpp 复制代码
void String::insert(size_t pos, char ch)
{
	assert(pos < _size);
	if (_size == _capacity)
	{
		reserve(_capacity == 0 ? 4 : 2 * _capacity);
	}
	//挪动数据
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = ch;
	_size++;
}

字符串

cpp 复制代码
void String::insert(size_t pos, char* str)
{
	//pos 不能超出范围
	assert(pos <= _size);
	int len = strlen(str);
	//看看现在的空间够不够
	if (pos + len > _capacity)
	{
		//如果需要的不大于两倍容量按两倍开,不然要好多开好多
		reserve(pos + len > 2 * _capacity ? pos + len : 2 * _capacity);
	}
	size_t end = _size + len;
	//空间开好了,来挪动数据,把前面的挪到后边
	while (end > pos + len - 1)
	{
		_str[end] = _str[end - len];
		end--;;
	}
	//插入数据
	for (size_t i = 0; i < len; i++)
	{
		_str[pos + i] = str[i];
	}
	//处理完了,最后一步
	_size += len;

}

erase()

cpp 复制代码
	void String::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		//不需要对空间进行操作,直接挪动数据
		/*size_t end = _size - len;
		for (size_t i = 1; i < len; i++)
		{
			_str[pos + i] = _str[end];
			end++;
		}*/
		if (len >= _size - pos)
		{
			_str[pos] = '\0';
			_size = pos;
		}
		else
		{
			for (size_t i = pos + len; i <= _size; i++)
			{
				_str[i - len] = _str[i];
			}
			_size -= len;

		}
	}

String的查找

单个字符

cpp 复制代码
size_t String::find(char ch, size_t pos)
{
	assert(pos < _size);

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

	return npos;
}

字符串

cpp 复制代码
	size_t String::find(const char* str, size_t pos)
	{
		assert(pos < _size);

		const char* ptr = strstr(_str + pos, str);
		if (ptr == nullptr)
		{
			return npos;
		}
		else
		{
			return ptr - _str;
		}
	}

substr()

cpp 复制代码
String String::substr(size_t pos, size_t len)
{
	assert(pos < _size);
	if (len > _size - pos)//剩下的全取出来
	{
		len = _size - pos;
	}
	String sub;
	sub.reserve(len);
	for (int i = 0; i < len; i++)
	{
		sub += _str[pos + i];
	}
	return sub;//传值返回会在堆栈调用拷贝构造重新生成一个变量后再返回,没有写拷贝构造默认浅拷贝
}
相关推荐
工业甲酰苯胺1 小时前
C语言之输入输出
c语言·c++·算法
C++忠实粉丝1 小时前
计算机网络之NAT、代理服务、内网穿透、内网打洞
网络·c++·网络协议·计算机网络·http·智能路由器
片片叶2 小时前
C++(十四)
开发语言·c++
程序猿阿伟3 小时前
《C++巧铸随机森林:开启智能决策新境界》
开发语言·c++·随机森林
阿客不是客3 小时前
深入计算机语言之C++:STL之list的模拟实现
数据结构·c++·stl
祖坟冒青烟4 小时前
Qt 的构建系统
c++
uyeonashi5 小时前
【C++】刷题强训(day14)--乒乓球匡、组队竞赛、删除相邻数字的最大分数
开发语言·c++·算法·哈希算法
妈妈说名字太长显傻6 小时前
【C++】string类
开发语言·c++
丢丢丢丢丢丢~6 小时前
c++创建每日文件夹,放入每日日志
开发语言·c++
華華3556 小时前
读程序题...
开发语言·c++·算法