【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;//传值返回会在堆栈调用拷贝构造重新生成一个变量后再返回,没有写拷贝构造默认浅拷贝
}
相关推荐
DolphinDB1 小时前
如何在C++交易系统中集成高性能回测与模拟撮合
c++
筏.k2 小时前
C++ 网络编程(14) asio多线程模型IOThreadPool
网络·c++·架构
爱喝茶的小茶3 小时前
周赛98补题
开发语言·c++·算法
OpenC++3 小时前
【C++】备忘录模式
c++·设计模式·备忘录模式
小庞在加油4 小时前
《dlib库中的聚类》算法详解:从原理到实践
c++·算法·机器学习·数据挖掘·聚类
ComputerInBook4 小时前
C++ 标准模板库算法之 transform 用法
开发语言·c++·算法·transform算法
2301_803554527 小时前
c++中类的前置声明
java·开发语言·c++
LyaJpunov9 天前
深入理解 C++ volatile 与 atomic:五大用法解析 + 六大高频考点
c++·面试·volatile·atomic
小灰灰搞电子10 天前
Qt PyQt与PySide技术-C++库的Python绑定
c++·qt·pyqt
时空自由民.10 天前
C++ 不同线程之间传值
开发语言·c++·算法