string【2】模拟实现(超详解哦)

12 篇文章 0 订阅

引言(实现概述)

在上一篇文章中,我们介绍了string类的使用:
戳我康string类的使用详解哦

在这里插入图片描述

在本篇文章中就要来模拟实现一下string类,以帮助我们更好的理解与使用string

在我们模拟实现的string中,要具备库中string所具有的主要接口,例如:默认成员函数、迭代器、容量、元素访问、运算符重载、非成员函数。其中只实现这些函数的常用重载形式。

我们将模拟实现的string类放在我们创建的命名空间内,以防止与库中的string发生命名冲突。在以后的STL模拟实现时,也会将其放在命名空间内。

string类的实现与之前C语言部分的顺序表类似,结合类和对象的知识,这个string类的属性有:指向堆中一块用于存放字符序列的空间的指针(_str)、字符序列的字符个数(_size)、字符序列的容量(_capacity

string类方法实现

默认成员函数

构造函数

在构造函数部分,就只实现用常量字符串构造string对象

在这个构造函数中,参数类型就是const char*,给这个参数一个空串""作为缺省值;
在函数内部首先assert判断参数是否为空指针;
然后令_size的值等于常量字符串str的长度,令_capacity等于_size的值;
然后new一块空间,将这块空间的指针赋给_str,这里需要注意的是,strlen计算字符串长度时,是以'\0'为结束标志的,所以在动态开辟空间时,需要开辟_capacity + 1个char的空间
最后使用memcpy将常量字符串中的数据拷贝到刚刚申请的空间中(这里拷贝时也需要将'\0'拷贝进去,所以要拷贝_size + 1 个字节):

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

拷贝构造

拷贝构造是构造函数的重载,参数类型为const string&如果不是引用就会导致无穷递归调用

实现拷贝构造时,不能直接将原对象的_str直接赋值给新对象的_str,否则就会导致浅拷贝,这样在析构时,同一块空间就要被析构两遍,显然就会导致崩溃。所以我们需要新申请一块空间后将原对象的字符序列拷贝到新的空间

先将原对象的_size 与 _capacity 直接赋值给新对象
然后new一块空间,将这块空间的指针赋给_str,这里需要注意的是,在我们实现的string类中,_capacity是不包括'\0'的,所以在动态开辟空间时,需要开辟_capacity + 1个char的空间
最后,使用memcpy将原对象中的字符序列拷贝到刚刚新开辟的空间中。

这里需要注意的是,C字符串的结束标志为'\0',但string对象没有结束标志,它的数据个数就是_size的值,所以当这个字符序列的中间出现'\0'时,使用strcpy就不会拷贝'\0'后面的数据,所以这里使用包括后面都使用memcpy

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

赋值运算符重载

在实现赋值运算符重载时,也存在深浅拷贝的问题,我们当然可以像上面的拷贝构造那样申请一块空间然后完成拷贝,但是那样的写法有点麻烦,于是就有了现代版本:

现代版本的参数类型为string,而不是引用,这就使得string对象在传参时会生成一个临时对象,我们将这个临时对象与要替换的对象*this互换,就实现了将一个对象赋值到了*this,最后返回*this即可,临时对象会在函数栈帧销毁时析构。
(这里的交换需要用到swap函数,这个函数后面会实现)

    //string& operator=(const string& s);   //老版本
    string& operator=(string s)
    {
		swap(s);
		return *this;
	}

析构函数

析构函数只需使用delete[] 释放_str指向的堆中的空间即可,还可以顺带将_size_capacity置0:

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

迭代器

前面提到过,string的迭代器就是原生指针,所以string中的 iterator就是char*const_iterator 就是const char* ,我们只需要使用typedefchar*const char* 重命名即可

    typedef char* iterator;
    typedef const char* const_iterator;

需要注意的是,因为在string类外也需要使用迭代器,所以这样的重命名应在pubilc中。

begin

begin获取的是字符序列首元素的地址,有两个重载版本,即对于非const对象返回iterator,对于const对象返回const_iterastor,首元素的地址就是_str

需要注意的是:const版本需要使用const修饰this指针

	string::iterator begin()
	{
		return _str;
	}
	string::const_iterator begin()  const
	{
		return _str;
	}

end

end获取的是字符序列最后一个元素下一个位置的地址,有两个重载版本,即对于非const对象返回iterator,对于const对象返回const_iterastor,最后一个元素下一个位置的地址就是_str + _size

	string::iterator end()
	{
		return _str + _size;
	}
	string::const_iterator end() const
	{
		return _str + _size;
	}

容量

size、capacity、empty

这三个成员函数的实现逻辑类似:
size用于获取string对象中字符序列的元素个数,返回_size即可;
capacity用于获取string对象的容量,返回_capacity即可;
empty用于判断string对象是否为空,若为空返回true,否则返回false:

	size_t size() const
	{
		return _size;
	}
	
	size_t capacity() const
	{
		return _capacity;
	}
	
	bool empty() const
	{
		if (_size == 0)
		{
			return true;
		}
		return false;
	}

reserve

reserve用于修改string对象的容量,这个函数只有一个参数n,表示要扩容到多少个char。

reserve在扩容时,当n小于当前对象的容量时,reserve会将容量调整为比n大的值,所以在模拟实现时,当n小于容量时,将不做任何事。所以先判断n是否大于_capacity

C++中使用new不能像realloc一样实现扩容,必须使用new新开一块空间(开空间时,由于_capacity没有包括'\0',所以要开n + 1个char的空间);
再将原空间中的数据拷贝到新空间,然后释放原空间;
然后使_str指向新空间;
最后将_capacity的值改为n

	void reserve(size_t n)
	{
		if (n > _capacity)			
		{
			char* newstr = new char[n + 1]{ 0 };
			memcpy(newstr, _str, _size + 1);
			delete[] _str;
			_str = newstr;
			_capacity = n;
		}
	}

resize

resize用于修改string对象中字符序列的个数,当参数n小于size就删,大于size则用指定的字符c补足。

首先判断,当n大于_size的值时,就需要扩容,复用reserve扩容至n
然后循环,将下标为_sizen - 1位置的元素改为指定的c
最后在末尾加上'\0',并更新_size的值

n小于_size的值时,直接在下标为n的位置加上'\0',并更新_size即可:

	void resize(size_t n, char c)
	{
		if (n > _size)
		{
			if (n > _capacity)
			{
				reserve(n + _size);
			}
			for (size_t i = _size; i < n; ++i)
			{
				_str[i] = c; //这里的[]是访问数组元素,并非运算符重载的调用,所以不会越界
			}
			_size = n;
			_str[_size] = '\0';
		}
		else
		{
			_size = n;
			_str[_size] = '\0';
		}
	}

访问元素

operator[]

通过重载[]可以实现像数组下标一样访问string对象中字符序列的元素,有两个重载版本,即对普通对象与const对象。函数有一个参数index即要访问元素的下标。

首先assert判断参数index是否越界;
然后返回_str[index]即可:

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

修改

insert

insert实现将一个字符或字符串插入到string对象的pos位置,实现两个重载版本,即在pos位置插入一个字符与一个字符串:

插入字符

首先assert判断pos是否越界,pos为无符号整数,所以只需要判断是否大于_size即可;
然后当_size等于_capacity时,即空间已满,需要扩容;
扩容时,当_capacity的值为0时扩容到4,不为0时二倍扩容;

然后就需要循环,将pos位置后的数据全部向后移动(需要注意的是循环的终止条件,当pos为0时,若end的初始值为_size且为endend + 1赋值,循环的终止条件就为end >= pos而pos为size_t,当end与pos比较时,会转化为size_t而永远不可能小于0,故end的初始值为_size + 1,将end + 1给前赋值,终止条件就为end > pos);
最后将c填充到pos位置,并更新_size

	string& insert(size_t pos, char c)
	{
		assert(pos <= _size);

		if (_size == _capacity)
		{
			if (_capacity != 0)
			{
				reserve(2 * _capacity);
			}
			else
			{
				reserve(4);
			}
		}
		size_t end = _size + 1;
		while (end > pos)   //pos为size_t,当end与pos比较时,会转化为size_t而永远不可能小于0.故将end+1,后给前赋值
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = c;
		++_size;
		return *this;
	}
插入字符串

插入字符串的逻辑与插入字符类似:

首先判断pos是否越界,并判断是否需要扩容,当容量小于_size + len时就需要扩容(len 为插入字符串的长度,这里可以直接调用reserve,因为reserve中会判断参数是否大于原容量);
然后将pos位置后的数据全部向后移动len个位置(依旧需要注意终止条件:必须为 end > pos + len - 1 ,若为end >= pos+len时,当在0位置插入一个空串就会导致死循环,因为无符号整型不可能小于0。当为end > pos + len - 1时,遇到上面的情况,0 - 1为 -1,对无符号整型就是一个很大的数,将直接不进入循环

然后循环将字符串中的数据拷贝到pos位置,并更新_size

	string& insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		reserve(len + _size);
		size_t end = _size + len;
		
		while (end > pos + len - 1)   //写成end >= pos+len就会有问题(在0位置插一个"")
		{
			_str[end] = _str[end - len];
			--end;
		}
		for (size_t i = 0; i < len; ++i)
		{
			_str[pos + i] = str[i];
		}
		_size += len;
		return *this;
	}

append

append实现在string对象后追加一个字符串:

首先判断str是否为空指针,并判断是否需要扩容(当newlenth > _capacity时即需要扩容,当然也可以交给reserve中判断);
然后使用strcpy将str中的数据拷贝到_str + _size的后面(这里不需要使用memcpy,因为这里的字符串拷贝就是按照'\0'为结束标志的);
最后更新_size

	//尾追加
    void append(const char* str)
	{
		assert(str);
		size_t newlenth = _size + strlen(str);
		if (newlenth > _capacity)
		{
			reserve(newlenth);
		}

		strcpy(_str + _size, str);
		_size = newlenth;
	}

push_back

push_back用于在string对象末尾添加一个字符

实现时首先判断是否需要扩容(与insert插入字符时的逻辑一致);
然后将c放在_str_size位置,并更新_size
最后需要手动补上'\0'

	//尾插
    void push_back(char c)
	{
		if (_size == _capacity)
		{
			if (_capacity != 0)
			{
				reserve(2 * _capacity);
			}
			else
			{
				reserve(4);
			}
		}
		
		_str[_size] = c;
		++_size;
		_str[_size] = '\0';
	}

operator+=

operator+=即在string对象的末尾追加数据,实现两个重载版本,即追加字符与追加字符串:

实现时,复用appendpush_back即可(当然,上面的append与push_back也可以借助insert实现)

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

erase

erase实现删除pos位置上的len个元素

len等于npos时,即将pos位置后全删,将pos位置改为'\0'并更新_size即可(npos为无符号整型的-1,即一个很大的数);
否则,就需要循环,将pos + len位置后的数据全部向前移动 len个位置,覆盖原数据实现删除,最后更新_size

	// 删除pos位置上的len个元素,并返回
	string& erase(size_t pos, size_t len)
	{
		if (len == npos)
		{
			_size = pos;
			_str[_size] = '\0';
		}
		for (size_t i = 0; i < _size - pos; ++i)
		{
			_str[pos + i] = _str[pos + len + i];
		}
		_size -= len;
		return *this;
	}

clear

clear即清空string对象中的数据,只需要将0位置改为'\0',并将_size更新为0即可:

    void clear()
	{
		_size = 0;
		_str[_size] = '\0';
	}

swap

swap实现交换两个string对象

在之前的swap函数,包括算法库中的swap函数均是通过临时变量的方式交换的。但是对于string对象而言,要创建临时对象通过三次赋值来交换的话,就会产生三次深拷贝,十分影响效率

在实现交换时,其实没有必要出现深拷贝,string对象中有_str指向一块空间中存储数据,只要交换string对象中的这个存储数据的指针即可实现交换数据,所以将string的对象的属性分别实现交换即可,交换_size_capacity_str即可

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

比较运算符重载

在实现比较运算符重载时,我们其实只需要实现两种,即<==,其他的运算符重载通过复用这两种即可,此类函数都需要使用const修饰this以适配const对象:

operator<

operator<实现两个string对象的比较:当第一个对象小于第二个对象时,返回true,否则返回false

我们可以for循环逐字节判断,循环的终止条件为两个string对象_size的较小值;
当遇到对应字符不相等的情况时,直接返回truefalse
当循环结束,说明前面的元素都是相等的。此时,哪个对象的_size较大,则该对象较大:

	bool operator<(const string& s) const
	{
		for (size_t i = 0; i < (_size < s._size ? _size : s._size); ++i)
		{
			if (_str[i] < s._str[i])
				return true;
			if (_str[i] > s._str[i])
				return false;
		}
		if (_size < s._size)
			return true;
		else
			return false;
	}

operator==

operator==用于判断两个string对象是否相等,相等返回true,否则返回false

当两个string对象的_size不同时,直接返回false
然后循环遍历两个对象,遇到对应位置不相同的,直接返回false
最后,出循环说明均相等,返回true

	bool operator==(const string& s) const
	{
		if (_size != s._size)
			return false;

		for (size_t i = 0; i < _size; ++i)
		{
			if (_str[i] != s._str[i])
				return false;
		}
		return true;
	}

其他

其他函数,根据比较的逻辑复用即可:

	bool operator<=(const string& s) const
	{
		if (*this < s || *this == s)
			return true;
		else
			return false;
	}
	bool operator>(const string& s) const
	{
		if (!(*this <= s))
			return true;
		else
			return false;
	}
	bool operator>=(const string& s) const
	{
		if (!(*this < s))
			return true;
		else
			return false;
	}
	bool operator!=(const string& s) const
	{
		if (!(*this == s))
			return true;
		else
			return false;
	}

查找

find用于在string对象中查找是否存在某字符或某字符串,若存在就返回其第一次出现的位置,否则返回npos,有两个重载版本,即查找字符与查找字符串:

查找字符

查找字符时,即使用循环从pos位置开始遍历string对象中的数据,当遇到与c相等的字符时,就返回该位置。若出循环,就表示没有找到,返回npos

	// 返回c在string中第一次出现的位置
	size_t find(char c, size_t pos) const
	{
		assert(pos < _size);
		for (size_t i = pos; i < _size; ++i)
		{
			if (_str[i] == c)
			{
				return i;
			}
		}
		return npos;
	}

查找字符串

在string对象中查找字符串时,可以使用之前C语言时学过的strstr函数,用于查找字串。(当第2个参数为第1个参数的子串时,返回在其中的第一个位置的地址,否则返回空指针)

首先assert判断s是否为空指针,以及pos是否越界;
然后调用strstr,第一个参数为_str,第二个参数为s,并创建一个指针pchar来接收返回值;

pchar为空时,返回npos,当pchar - _str的值不小于pos时,返回该差,否则返回npos

	// 返回子串s在string中第一次出现的位置
	size_t find(const char* s, size_t pos) const
	{
		assert(s);
		assert(pos < _size);
		char* pchar = strstr(_str, s);
		if (pchar == nullptr)
		{
			return npos;
		}
		if ((size_t)(pchar - _str) >= pos)
		{
			return pchar - _str;
		}
		return npos;
	}

非成员函数

非成员函数中只实现流插入与流提取运算符的重载(operator<<operator>>):

在之前的日期类实现中,我们使用友元函数,实现在这两个函数中可以访问对象的属性。但在string类中,由于之前实现过访问元素的operator[],所以可以不使用友元就可以实现在这两个函数中访问string对象的元素

operator<<

operator<<中, 我们只需要将string对象s中的元素依次流入到ostream的对象_cout即可:

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

operator>>

在向内存中输入数据时,我们当然可以逐字符的+=,但是这样会造成多次的扩容而影响效率。

我们可以直接创建一个128字节的数组来转存数据,当在这个数组中存满后再将这个数组中的数据+=到string对象中,然后清空数据继续接收数据,等到全部接收完毕后,将其中剩余的元素再**+=**到string对象后即可:

	istream& operator>>(istream& _cin, string& s)
	{
		s.clear();
		char ch = _cin.get();
		//清除缓冲区中的空格与换行
		while (ch == ' ' || ch == '\n')
		{
			ch = _cin.get();
		}

		//定义一个128的字符数组
		char temp[128] = { 0 };
		int i = 0;
		while (ch != ' ' && ch != '\n')
		{
			if (i == 127)
			{
				s += temp;
				i = 0;
			}
			temp[i] = ch;
			++i;

			ch = _cin.get();
		}

		//如果i>0 即temp中还有数据,将其转存即可
		if (i > 0)
		{
			temp[i] = '\0';
			s += temp;
		}

		return _cin;
	}

源码概览

#include<iostream>
#include<cassert>
using namespace std;

namespace qqq
{
    class string
    {
    public:
        typedef char* iterator;
        typedef const char* const_iterator;

    public:
        string(const char* str = "")
		{
			assert(str);
			_size = strlen(str);
			_capacity = _size;
			_str = new char[_capacity + 1];

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

			memcpy(_str, s._str, s._size + 1);
		}
        //string& operator=(const string& s);   //老版本

        string& operator=(string s)
		{
			swap(s);
			return *this;
		}
        ~string()
		{
			_size = 0;
			_capacity = 0;
			delete[] _str;
		}

        //
        // iterator
		string::iterator begin()
		{
			return _str;
		}
		string::iterator end()
		{
			return _str + _size;
		}
		string::const_iterator begin()  const
		{
			return _str;
		}
		string::const_iterator end() const
		{
			return _str + _size;
		}

        /
        // modify
        // 在pos位置上插入字符c/字符串str,并返回
		string& insert(size_t pos, char c)
		{
			assert(pos <= _size);

			if (_size == _capacity)
			{
				if (_capacity != 0)
				{
					reserve(2 * _capacity);
				}
				else
				{
					reserve(4);
				}
			}

			size_t end = _size + 1;
			while (end > pos)   //pos为size_t,当end与pos比较时,会转化为size_t而永远不可能小于0.故将end+1,后给前赋值
			{
				_str[end] = _str[end - 1];
				--end;
			}
			_str[pos] = c;
			++_size;
			return *this;
		}
		string& insert(size_t pos, const char* str)
		{
			assert(pos <= _size);

			size_t len = strlen(str);
			reserve(len + _size);

			size_t end = _size + len;
			while (end > pos + len - 1)   //写成end >= pos+len就会有问题(在0位置插一个"")
			{
				_str[end] = _str[end - len];
				--end;
			}
			for (size_t i = 0; i < len; ++i)
			{
				_str[pos + i] = str[i];
			}

			_size += len;
			return *this;
		}
		// 删除pos位置上的元素,并返回
		string& erase(size_t pos, size_t len)
		{
			if (len == npos)
			{
				_size = pos;
				_str[_size] = '\0';
			}
			for (size_t i = 0; i < _size - pos; ++i)
			{
				_str[pos + i] = _str[pos + len + i];
			}
			_size -= len;
			return *this;
		}
		//尾插
        void push_back(char c)
		{
			if (_size == _capacity)
			{
				if (_capacity != 0)
				{
					reserve(2 * _capacity);
				}
				else
				{
					reserve(4);
				}
			}

			_str[_size] = c;
			++_size;
			_str[_size] = '\0';
		}
        string& operator+=(char c)
		{
			push_back(c);
			return *this;
		}
		//尾追加
        void append(const char* str)
		{
			assert(str);
			size_t newlenth = _size + strlen(str);
			if (newlenth > _capacity)
			{
				reserve(newlenth);
			}

			strcpy(_str + _size, str);
			_size = newlenth;
		}
        string& operator+=(const char* str)
		{
			append(str);
			return *this;
		}
        void clear()
		{
			_size = 0;
			_str[_size] = '\0';
		}
        void swap(string& s)
		{
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
			std::swap(_str, s._str);
		}
        const char* c_str() const
		{
			return _str;
		}

        /
        // capacity
		size_t size() const
		{
			return _size;
		}
		size_t capacity() const
		{
			return _capacity;
		}
		bool empty() const
		{
			if (_size == 0)
			{
				return true;
			}
			return false;
		}
		void resize(size_t n, char c)
		{
			if (n > _size)
			{
				if (n > _capacity)
				{
					reserve(n + _size);
				}

				for (size_t i = _size; i < n; ++i)
				{
					_str[i] = c; //这里的[]是访问数组元素,并非运算符重载的调用,所以不会越界
				}

				_size = n;
				_str[_size] = '\0';
			}
			else
			{
				_size = n;
				_str[_size] = '\0';
			}
		}
		void reserve(size_t n)
		{
			if (n > _capacity)
			{
				char* newstr = new char[n + 1]{ 0 };
				memcpy(newstr, _str, _size + 1);
				//strcpy(newstr, _str);

				delete[] _str;
				_str = newstr;

				_capacity = n;
			}
		}

        /
        // access
		char& operator[](size_t index)
		{
			assert(index < _size);
			return _str[index];
		}
		const char& operator[](size_t index) const
		{
			assert(index < _size);
			return _str[index];
		}

        /
        //relational operators
		bool operator<(const string& s) const
		{
			for (size_t i = 0; i < (_size < s._size ? _size : s._size); ++i)
			{
				if (_str[i] < s._str[i])
					return true;
				if (_str[i] > s._str[i])
					return false;
			}
			if (_size < s._size)
				return true;
			else
				return false;
		}
		bool operator==(const string& s) const
		{
			if (_size != s._size)
				return false;

			for (size_t i = 0; i < _size; ++i)
			{
				if (_str[i] != s._str[i])
					return false;
			}
			return true;
		}
		bool operator<=(const string& s) const
		{
			if (*this < s || *this == s)
				return true;
			else
				return false;
		}
		bool operator>(const string& s) const
		{
			if (!(*this <= s))
				return true;
			else
				return false;
		}
		bool operator>=(const string& s) const
		{
			if (!(*this < s))
				return true;
			else
				return false;
		}
		bool operator!=(const string& s) const
		{
			if (!(*this == s))
				return true;
			else
				return false;
		}

		// 返回c在string中第一次出现的位置
		size_t find(char c, size_t pos) const
		{
			assert(pos < _size);
			for (size_t i = pos; i < _size; ++i)
			{
				if (_str[i] == c)
				{
					return i;
				}
			}
			return npos;
		}
		// 返回子串s在string中第一次出现的位置
		size_t find(const char* s, size_t pos) const
		{
			assert(s);
			assert(pos < _size);
			char* pchar = strstr(_str, s);
			if (pchar == nullptr)
			{
				return npos;
			}
			if ((size_t)(pchar - _str) >= pos)
			{
				return pchar - _str;
			}
			return npos;
		}

    private:
        char* _str;
        size_t _capacity;
        size_t _size;

        const static size_t npos;
    };

	const size_t string::npos = -1;

	ostream& operator<<(ostream& _cout, const string& s)
	{
		for (size_t i = 0; i < s.size(); ++i)
		{
			_cout << s[i];
		}
		return _cout;
	}
	istream& operator>>(istream& _cin, string& s)
	{
		s.clear();
		char ch = _cin.get();
		//清除缓冲区中的空格与换行
		while (ch == ' ' || ch == '\n')
		{
			ch = _cin.get();
		}

		//定义一个128的字符数组
		char temp[128] = { 0 };
		int i = 0;
		while (ch != ' ' && ch != '\n')
		{
			if (i == 127)
			{
				s += temp;
				i = 0;
			}
			temp[i] = ch;
			++i;

			ch = _cin.get();
		}

		//如果i>0 即temp中还有数据,将其转存即可
		if (i > 0)
		{
			temp[i] = '\0';
			s += temp;
		}

		return _cin;
	}
}

总结

到此,关于string类的模拟实现就介绍完了
相信通过模拟实现string类可以使我们更深入地理解string

如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

如果本文对你有帮助,希望一键三连哦

希望与大家共同进步哦

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 20
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿qiu不熬夜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值