c++ 库中string的简单实现

文章目录

  • string
    • 构造函数string()
    • 拷贝构造string()
    • 长度size()
    • 下标引用[]`//返回这个字符串
    • 类型间的转化c_str
    • 迭代器iterator、begin、end
    • print打印
    • 扩容reserve
    • push_bacak尾插
    • append尾插入一个字符串
    • +=尾部插入
    • = 赋值
    • insert在pos位置插入一个字符或者字符串
    • erase从pos位置开始删除len个字符
    • swap 交换类
    • find 找一个字符在一个对象里面的下标
    • finde找子串
    • substr从pos位置开始 返回len长度的的字符串
    • ~stirng()析构函数
    • >>流提取
    • 流插入<<

string

构造函数string()

这里直接开一个新空间 把传过来的字符内容赋值到 _str
用const 不管是const和非const 都可以通用
初始化的时候要放\0到 字符里面
如果不放\0那么 穿空串会报错

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

拷贝构造string()

把传过来的 对象 用新空间来接受 把 新空间赋给 _str
开空间是为了让 _str有足够的空间来接受

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

长度size()

因为查看长度是不会改变的,所以用const来定义

size_t size() const
{
	return _size;
}

下标引用[]`//返回这个字符串

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

用引用传值,来减少时间
下标引用需要重载2个版本,一个是const的版本,一个是非const的
const的是给一些不需要改变的成员函数和用const定义的对象使用
非const 是给一些需要改变下标内的成员函数和普通对象使用

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

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

类型间的转化c_str

类型间的转化

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

迭代器iterator、begin、end

在string中 迭代器iterator其实就是指针
iterator需要重载 const和非const的版本
begin: 指向 对象的开始位置
end: 指向 对象的末尾位置
同样begin和end也需要重载2个版本
重载2个版本是因为有静态变量

//string 迭代器
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;
}

print打印

void print_str(const string& str)
{
	for (size_t i = 0; i < _size; i++)
	{
		cout<<str[i]<<" ";
	}
	cout << endl;
}

扩容reserve

如果需要扩容的对象原本可存长度小于需要的长度那么直接扩容
如果是想缩容的话 我们不理会

void reserve(size_t n = 0)
{
	//如果n>_capacity 那么开辟一块新空间 把 str里面的值拷贝给tmp 
	// 把旧空间释放
	// 把tmp 赋给 _str
	if (n > _capacity)
	{
		char* tmp = new char[n+1];
		strcpy(tmp, _str);
		delete[] _str;
		_str = tmp;
		_capacity = n;
	}

}

push_bacak尾插

把一个字符直接尾插到对象里面
同样如果位置不够也是需要扩容的

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

append尾插入一个字符串

把一个字符串插入到对象里面
如果位置不够也需要扩容

void append(const char* str)
{
	assert(str);
	size_t pos = strlen(str);
	//如果 插入后越界就会出问题
	if (_size + pos >= _capacity)
	{
		reserve(_size+pos);
	}
	strcpy(_str + _size, str);
	_size += pos;
}

+=尾部插入

+=是可以插入一个字符和一个字符串
所以要重载2个版本

string& operator+= (char c)
{
	push_back(c);
	return *this;
}

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

= 赋值

开辟一个新空间 让传过来的对象 赋值到 新空间上
把_str指向新空间
把旧的空间释放

string& operator=(const string& str)
{
	//如果拷贝的时候 str的内容长度大于 this的内如长度 
	//那么则需要 新开辟一个空间
	//让这个 空间让 str内容一样
	//释放原来的旧this 内容
	//把this指向那段新开辟的空间
	if (this != &str)
	{
		char* tmp = new char[str._capacity + 1];
		strcpy(tmp, str._str);
		delete[] _str;
		_size = str._size;
		_capacity = str._capacity;
		_str = tmp;
	}
	return *this;
}

insert在pos位置插入一个字符或者字符串

因为是需要插入一个字符或者字符串
那么肯定是需要重载2个版本的
还要判断扩容,以及是否插入之后会越界

void insert(size_t pos, char ch)
{
	assert(pos < _size);
	if (_size == _capacity)
	{
		size_t newCapacity = _capacity == 0 ? 4 : _capacity * 2;
		reserve(newCapacity);
	}
	//这里要指向 size+1 不然的话 有可能pos ==1 end会变成 -1
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		end--;
	}
	_str[pos] = ch;
	_size++;
}
//在pos位置插入一个字符串
void insert(size_t pos, const char * s)
{
	assert(pos < _size);
	size_t sz = strlen(s);
	if (_size + sz > _capacity)
	{
		reserve(_size + sz);
	}
	size_t end = _size;
	//把 数组中末尾位置的数据挪动到end+sz位置处
	//给s数组 预留sz个位置
	while (end >= (int)pos)
	{
		_str[end+sz] = _str[end];
		end--;
	}
	strncpy(_str + pos, s,sz);
	_size += sz;
}

erase从pos位置开始删除len个字符

如果从pos+len >原对象的长度的话那么 直接 插入一个\0到pos位置就可以了
如果不大于 那么就需要把 _str+pos+len位置的给到 _str+pos位置

void erase(size_t pos, size_t len = npos)
{
	assert(pos <= _size);
	//如果大于或 等于 _size - pos 的长度 那么 直接插入一个'\0' 因为他不管怎么样 都是删完
	if (len==npos||npos >= _size - pos)
	{
		_str[pos] = '\0';
	}
	else
	{
		//这里拷贝的时候会从pos位置开始 拷贝 pos+len位置的值到的结束
		strcpy(_str + pos, _str + pos+len);
		_size -= len;
	}
}

swap 交换类

直接调用库里面的模版

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

find 找一个字符在一个对象里面的下标

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

finde找子串

找到子串返回 子串在 _str 的起始坐标

size_t find(const char * str,size_t pos = 0)
{
	//如果找到子串赋给tmp
	const char * tmp = strstr(_str+pos, str);
	if (tmp == nullptr)
	{
		return npos;
	}
	//如果找到了 返回下标
	//指针-指针=他们之间的差值
	else
	{
		return tmp - _str;
	}
}

substr从pos位置开始 返回len长度的的字符串

需要开新空间接受这段字符串
返回值的时候如果没有写拷贝构造那么他会报错
原因是因为他指向的那块空间和函数里的新开辟一样
如果出了函数那么 创造的那个新空间会调用析构函数
那么会出现指向野指针的情况

string substr(size_t pos = 0, size_t len = npos)
{
	assert(pos < _size);
	size_t end = pos + len;
	if (len == npos||end>=_size)
	{
		end = _size;
	}
	string str;
	//开空间 让str可以存储
	str.reserve(end - pos);
	for (size_t i = 0; i < end; i++)
	{
		str += _str[i];
	}

	//这里要调拷贝构造才能正常返回
	//如果这么没写拷贝构造
	//那么会默认浅拷贝(值拷贝)
	//浅拷贝:让 临时拷贝的对象指向跟str一样的空间
	//但是出了函数 str会 析构
	//会把临时对象指向的空间变成 野指针
	return str;
}

~stirng()析构函数

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

>>流提取

流插入(>>)的时候需要定义一个新的数组和一个字符
用字符存到数组里面
把数组尾插到串过来的对象里面
最后在 数组的尾部在插入一个\0来终止字符
如果直接插入的话会出现报错
这个报错就是出了这个函数这个 传过来的会调用析构


	istream& operator>> (istream& in, string& str)
	{
		char ch = in.get();
		char arr[128];
		int a = 0;
		while (ch!=' '&&ch!='\n')
		{
			arr[a++] = ch; 
			if (a == 127)
			{
				arr[a] = '\0';
				str += arr;
				a = 0;
			}
			ch = in.get();	
		}
		if (a != 0)
		{
			arr[a] = '\0';
			str += arr;
		}
		return in;
	}

流插入<<

直接用for来打印

ostream& operator<< (ostream& os, const string& str)
{
	for (size_t i = 0; i < str.size(); i++)
	{
		os << str[i];
	}
	return os;
}

你可能感兴趣的:(C++,c++,算法,c语言,数据结构,stl,容器,后端)