目录
  • 模拟实现一个string类
    • 成员变量
    • 构造函数
    • 遍历
    • 与容量相关的成员函数
    • 运算符的重载
    • 修改器
    • 常用的几个字符串函数
  • 总结

    STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。

    STL的六大组件:容器、迭代器、适配器、空间配置器、仿函数、算法。

    string的行为与普通容器类似,但是并不能说它是个容器,因为无法接受所有类型的数据。

    string是表示字符串的字符串类。

    string在底层实际是:basic_string模板类的别名

    typedef basic_string<char, char_traits, allocator>string;

    STL中的string你了解吗

    STL中的string你了解吗

    头文件: #include<string>

    模拟实现一个string类

    首先成员变量最少需要一个字符类型的指针、字符串的大小、总共能存多少有效字符。

    其次还需要构造函数、遍历的方法、增删查改、运算符重载等。

    成员变量

    class MyString
    {
    private:
    	char *_str;//字符串指针
    	size_t _size;//字符串大小
    	size_t _capacity;//总共能存多少有效字符,不包括'\0'
    	static size_t npos;//迭代器相关
    }
    size_t MyString:: npos = -1;

    构造函数

    //构造函数
    MyString(const char* str = "")//缺省参数
    {
    	_size = strlen(str);//初始化
    	_capacity = _size;
    	_str = new char[_capacity + 1];//'\0'的空间+1
    	strcpy(_str, str);
    }
    //析构函数
    ~MyString()
    {
    	delete[] _str;//释放内存
    	_str = nullptr;//将指针置空
    	_size = 0;//清理工作
    	_capacity = 0;
    }
    //拷贝构造函数
    MyString(const MyString& str)
    {
    	_size = str._size;
    	_capacity = str._capacity;
    	_str = new char[_capacity + 1];
    	strcpy(_str, str._str);
    }
    //赋值运算符重载
    MyString& operator=(const MyString& str)
    {
    	if (_str != str._str)
    	{
    		delete[] _str;
    		_size = str._size;
    		_capacity = str._capacity;
    		_str = new char[_capacity + 1];
    		strcpy(_str, str._str);
    	}
    	return *this;
    }
     

    遍历

    1、[ ]的重载

    我们在C语言中使用字符串时是可以通过[ ]进行随机访问的,所以在设计string类时,通过重载[ ]实现相同的效果。

    STL中的string你了解吗

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

    需要两种类型的operator[ ],一个是针对非const类型对象,一个是针对const类型对象。const类型的对象是没有办法调用非const修饰*this的成员函数和重载,原因:权限扩大了。

    2、迭代器

    STL中的string你了解吗

     除了用[ ]来遍历类里面的字符串以外,另外的方法就是通过迭代器。

    对于string的迭代器我们只需要宏定义一下

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

    测试一下代码

    void test_string()
    {
    	MyString ms;
    	ms = "123";
    	MyString::iterator it = ms.begin();
    	while (it != ms.end())
    	{
    		cout << *it << endl;
    		it++;
    	}
    }

    STL中的string你了解吗

    rbegin与rend是反向迭代器,即反向遍历字符串。

    前面带c的cbegin、cend等等是常字符串类型的对象

    const iterator cbegin()const
    {
    	return _str;
    }
    const iterator cend()const
    {
    	return _str + _size;
    }

    与容量相关的成员函数

    STL中的string你了解吗

    实现几个比较常用的函数接口

    //返回字符串大小
    size_t size()const
    {
    	return _size;
    }
    size_t capacity()const
    {
    	return _capacity;
    }
    //判断是否为空字符串
    bool empty()const
    {
    	return _size == 0;
    }
    //更改容量
    void reserve(size_t n = 0)
    {
    	if (n > _capacity)
    	{
    		char* tmp = new char[n + 1];
    		strcpy(tmp, _str);
    		delete[] _str;
    		_str = tmp;
    		_capacity = n;
    	}
    }
    //更改大小
    //resize分三种情况
    void resize(size_t n = 0,char ch = '\0')
    {
    	if (n >= 0 && n <= _size)
    	{
    		_size = n;
    		_str[_size] = '\0';
    	}
    	else if (n > _size && n <= _capacity)
    	{
    		for (size_t i = _size; i < n; i++)
    			_str[i] = ch;
    		_size = n;
    		_str[_size] = '\0';
    	}
    	else if (n > _capacity)
    	{
    		reserve(n);
    		for (size_t i = _size; i < n; i++)
    		{
    			_str[i] = ch;
    		}
    		_size = n;
    		_str[_size] = '\0';
    	}
    	else
    		assert(0);
    }

    size、capacityempty只需要设置成const类型,因为不需要修改内容。

    reserve只修改_capacity的大小。

    resize的实现需要分三种情况,当n的长度小于等于_size的时候,只需要修改一下_size的大小,然后把_size的位置设置为'\0'。当n的长度大于_size且小于_capacity的时候,需要新插入n-_size个ch;如果大于_capacity,说明需要重新开辟空间了,并插入n-_size个ch。

    运算符的重载

    1、+=的重载

    平常用string类的时候发现+=进行字符串拼接很方便。

    MyString& operator+=(const char* str)
    {
    	int len = strlen(str);
    	if (len + _size > _capacity)//判断是否超出容量
    	{
    		reserve(len + _size);
    	}
    	strcpy(_str + _size, str);
    	_size += len;
    	return *this;
    }
    MyString& operator+=(char ch)
    {
    	if (_size == _capacity)//扩容
        {
    	    size_t newcapacity = (_capacity) == 0 ? 2 : _capacity * 2;
    	    reserve(newcapacity);
        }
        _str[_size] = ch;
        _size++;
        _str[_size] = '\0';//尾插过后会把'\0给覆盖了,重新在最后一个位置补一个'\0'
    	return *this;
    }

    2、<< 和 >>的重载

    为了保持和标准输入输出的使用形式是一样的,建议在类外面重载<<和>>。

    //需要在类外面重载
    //输出流
    ostream& operator<<(ostream& out, const MyString& str)
    {
    	for (size_t i = 0; i < str.size(); i++)
    	{
    		out << str[i];
    	}
    	return out;
    }
    //输入流
    istream& operator>>(istream& in, MyString& str)
    {
    	while (1)
    	{
    		char ch = in.get();
    		if (ch != ' '&&ch != '\n')//cin遇到空格和'\n'会结束
    		{
    			str += ch;
    		}
    		else
    			break;
    	}
    	return in;
    }
     

    补充getline函数:遇到'\n'才结束

    用法:getline(cin,对象);

    //getline是遇到'\n'才结束
    istream& getline(istream& in, MyString& s)
    {
    	while (1)
    	{
    		char ch;
    		ch = in.get();//从缓存去读入所有输入字符
    		if (ch != '\n')
    		{
    			s += ch;
    		}
    		else
    			break;
    	}
    	return in;
    }

    修改器

    STL中的string你了解吗

    push_back尾插

    void push_back(char ch)//插入一个字符,尾插
    {
    	if (_size == _capacity)
    	{
    		size_t newcapacity = (_capacity) == 0 ? 2 : _capacity * 2;
    		reserve(newcapacity);
    	}
    	_str[_size] = ch;
    	_size++;
    	_str[_size] = '\0';//尾插过后会把'\0给覆盖了,重新在最后一个位置补一个'\0'
    }

    insert任意位置插入字符或者字符串

    MyString& insert(size_t pos, const char ch)
    {
    	assert(pos <= _size && pos >= 0);
    	if (_size == _capacity)
    	{
    		size_t newcapacity = _capacity == 0 ? 2 : 2 * _capacity;
    		reserve(newcapacity);
    	}
    	int end = _size;
    	while (end >= (int)pos)//为什么要强转,如果是头插,end最终=-1,
    	{					   //-1和无符号比较会向无符号转变成一个32位的最大值,成为死循环
    		_str[end + 1] = _str[end];
    		end--;
    	}
    	_str[pos] = ch;
    	_size++;
    	return *this;
    }
    MyString& insert(size_t pos, const char* str)
    {
    	assert(pos <= _size && pos >= 0);
    	size_t len = strlen(str);
    	if (_size+ len > _capacity)
    	{				
    		reserve(_capacity + len);
    	}
    	int end = _size;
    	while (end >= (int)pos)//往后挪
    	{
    		_str[end + len] = _str[end];
    		end--;
    	}
    	for (size_t i = 0; i < len; i++)
    	{
    		_str[pos + i] = str[i];
    	}
     
    	_size += len;
    	return *this;
    }

    erase删除

    //npos = -1(无符号整型最大值)
    MyString& erase(size_t pos, size_t len = npos)
    {
    	assert(pos >= 0 && pos < _size);
    	if (pos + len >= _size || len == npos)
    	{
    		_size = pos;
    		_str[_size] = '\0';
    	}
    	else
    	{
    		for (size_t i = 0; i < _size - len - pos; i++)
    		{
    			_str[i + pos] = _str[i + pos + len];
    		}
    		_size -= len;
    	}
    	_str[_size] = '\0';
    	return *this;
    }

    可以看出删除和任意位置插入还是挺费时间的,需要整体挪动字符串。

    常用的几个字符串函数

    findsubstrc_str也得掌握

    find的接口比较多,可以查找string类、查找char*的字符串也可以查找单个字符

    STL中的string你了解吗

    返回值为对应的下标,没找到返回npos。

    substr获得一个子串,返回值为string类型

    STL中的string你了解吗

    pos表示从哪里开始,len表示子串长度。

    c_str 将C++的字符串类转化成C语言中char*类型。

    总结

    本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。