string模拟实现

文章详细介绍了如何模拟实现C++中的string类,包括构造函数、拷贝构造、赋值重载、深拷贝与浅拷贝的概念和实现,以及成员函数如resize、push_back、insert、erase、find等的关键代码实现,并探讨了迭代器和内存管理的相关问题。
摘要由CSDN通过智能技术生成

1.回顾库函数

strcpy

strcpy 都是char 类型 考完就算完成

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);

	char* ret = dest;			//先对src后置++ 在解引用  后置++优先级高于* 这个写法\0也被拷贝,表达式结果为\0就结束
	while ((*dest++ = *src++))//判断*dest的结果是不是'\0',如果是就结束,会将源字符串中的 '\0' 拷贝到目标空间
	{
		;
	}
	return ret;

}

memcpy

void * memcpy ( void * destination, const void * source, size_t num );
涉及多种类型,可能是char* int*
所以传入形参num ,多少字节需要被拷贝
num
Number of bytes to copy.
size_t is an unsigned integral type.
其中就需要利用char*指针 1个字节一个字节的拷贝

要用此函数拷贝,要保证源和目的地都至少有num字节个空间

思路如图所示:
在这里插入图片描述

void* my_memcpy(void* dest, const void* src, size_t num)
{//如果source和destination有任何的重叠,复制的结果都是未定义的。
	assert(dest && src);
	void* ret = dest;
	//前->后
	while (num--)//循环num次
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;//++(char*)dest;如果换成(char*)dest++,dest++报错(“void *”: 未知的大小),因为char*转换是临时的
		src = (char*)src + 1;//++(char*)src;
	}
	return ret;
}

strcmp

strcmp能比较不同长度的字符串吗?可以,并且其中字符串提前结束后最后一位\0参与了对比,这和string里面的<复写有所区别,string里面\0不应该参与对比

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while(*str1 == *str2)
	{
		if (*str1 == '\0')//abc和abc相等情况判断,如果*str1 == \0 那么*str2也等于\0
			return 0;
		str1++;
		str2++;
	}
	return *str1 - *str2;//不相等看*str1 - *str2谁大 返回>0 <0
}

strstr

思路:
BF
暴力求解,定义下标i,j开始从头遍历,i和j相同就一起+1,遇到不同的字符就让j返回到子串0,i回到开始位置+1

时间复杂度O(m*n)
在这里插入图片描述

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	if (*str2 == '\0')//传入空字符串  直接返回str1
	{
		return (char*)str1;
	}

	const char* s1 = NULL;
	const char* s2 = NULL;
	const char* cp = str1;//记录可能的起始位置

	while (*cp)
	{
		s1 = cp;
		s2 = str2;//重置要查找子串的开始位置
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cp;
		}
		cp++;
	}
	return NULL;
}

2.回顾类和对象

哪些函数里会有this指针?

答:只要是类成员函数里面都有

this指针调用方法

this->push_back(); 等价于 (*this).push_back();

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

结论:只要是不修改this指针指向的对象内容的成员函数,都可以加上const

好处是 非const和const的对象都可以调用,但对于要修改this指向内容的对象加上const就没法改了

自己写了构造函数,编译器不会自动生成默认构造

2.1构造和拷贝构造都可以使用初始化列表进行初始化

在这里插入图片描述

2.2 C和C++区别:C中结构体里面没有函数方法

3.string 模拟实现

要注意_size和_capacity的更新
也要注意 成员函数插入或删除的一些函数 pos位置的范围 assert(pos<size)…等

构造函数

默认构造的错误写法

这种写法导致了 cout<< nullptr << endl的错误,输出nullptr会报错

然而cout << NULL << endl;并不会报错,NULL可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量,默认编译器认为NULL是0,要搞成指针必须(void*)0

在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

   string()
   :_size(0)
   ,_capacity(0)
   ,_str(nullptr)
   {   }

默认构造的正确写法

####  预备知识	

即使stirng对象是空,string认为也是要有一个\0的
string类 capacity不计算\0
c的字符数组,以\o为终止算长度
string不看\0,以size为终止算长度

	string()
	:_size(0)
	,_capacity(0)
	,_str(new char[1])
	{
		_str[0] = '\0';
	}

下面将默认构造和有参构造合并

此种实现的是最常用的构造 string (const char* s);
说人话就是用一个C字符串来构造string对象
开头罗列出了默认构造的几个错误缺省参数,原因如后面所示

这里并没有使用初始化列表进行初始化,因为都是内置类型,初始化列表也无法完成memcpy或其他可能的工作,所以干脆没用

根据_size+1也拷贝了\0,这里不用strcpy()而改用memcpy() 的原因是:遇到hello\0xxxxx这种string对象拷贝到\0就终止了就不能用strcpy了

c的字符数组,以\o为终止算长度
string不看\0,以size为终止算长度

//string(const char* s = '\0')//类型不匹配 char* = char
		//string(const char* s = nullptr) //下面strlen会崩 
		//string(const char* s = "\0") //内存有2个 \0 \0 没必要,但是正确,下面会开辟一个空间,strcpy会把\0拷贝到_str
		string(const char* s = "")//空字符串,默认1个\0,下面会开辟一个空间,strcpy会把\0拷贝到_str
		{
			_size = strlen(s);
			_capacity = _size;//string类 capacity不计算\0
			_str = new char[_size + 1];//为\0留一个位置
			//strcpy(_str, s);
			memcpy(_str,s,_size+1);//遇到hello\0xxxxx这种string对象拷贝就不能用strcpy了
		}

拷贝构造

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。

深浅拷贝

浅拷贝的问题
析构两次
一个对象的修改造成另一个对象一起改变

在这里插入图片描述

传统写法

形参中const string& s 加const 让const对象也可以传参,并且防止了this对象和形参赋值顺序可能发生颠倒

_str = new char[s._capacity + 1];为什么不开辟 s._size+1大小的空间?
因为目的就是拷贝和源相同capacity大小的空间,如果开s._size+1大小 _capacity就可能变小

这里我写出了一个错误,类似于C初阶冒泡函数形参传递无法在函数中sizeof出整个数组大小,我试图sizeof(s._str)求 整个数组的大小,这是做不到的,只有在同一个函数栈帧中才能sizeof(数组名)求出整个数组的大小,s是作为形参传入,s._str就类似于冒泡函数中的形参sort(int a[]) 里面sizeof(a)是求不出整个数组大小的

string(const string& s)
{
	_str = new char[s._capacity + 1];
	//memcpy(_str, s._str, sizeof(s._str));//形参传入的是指针,参考C初阶冒泡的错误写法
	memcpy(_str, s._str, s._size+1);
	_size = s._size;
	_capacity = s._capacity;
}

现代写法

错误写法

在这里插入图片描述

“正确”写法

思路:打工人是构造函数,利用s._str来构造对象tmp进行交换,除了注意要构造的变量未初始化随机值的问题,还要注意这种"hello\0world"特殊对象,如果是这种对象,根据string tmp(s._str)来构造只能构造到hello,\0后面的不会被拷贝
所以基于这个原因,我们对于拷贝构造还是用传统的不会有这个问题

		//hello\0world
		//现代写法
		string(const string& s)
			:_str(nullptr)
			, _size(0)
			, _capacity(0)
		{
			string tmp(s._str);

			swap(tmp);

		}

赋值重载

什么是内存泄漏:内存泄漏指因为疏忽或错误造成程序未能释放已经不再使用的内存的情况。

深浅拷贝

s1 = s2
浅拷贝造成的问题
析构两次
内存泄漏,原来s1有空间,浅拷贝是值拷贝,如图s1的空间无法被释放,造成内存泄漏
一个对象的修改造成另一个对象一起改变
在这里插入图片描述

类似于拷贝构造,但是拷贝构造不需要释放原来的数据空间
赋值重载要做的事情是释放原有的空间,开辟一块新空间,赋值另一个string对象的内容
在这里插入图片描述

//原有空间可能小,要扩容
//可能大,多的浪费了
//也可能刚好够
//不管原来到底是够还是大或者小,反正都是要释放原空间,开辟新空间
//s1 = s2
思路图
在这里插入图片描述

传统写法

自己开新空间,赋值s2的数据,再释放原有空间

赋值代表拷贝,这里不能开s._size+1个,比源对象的容量少,和拷贝构造一样

string& operator=(const string& s)
		{
			if (this != &s)//this指向s1 ,s1 != s才赋值
			{
				char* tmp = new char[s._capacity + 1];//赋值代表拷贝,这里不能开s._size+1个,比源对象的容量少
				memcpy(tmp,s._str, s._size+1);
				delete[] _str;
				_str = tmp;
				_size = s._size;
				_capacity = s._capacity;
			}
			return *this;
		}

现代写法

在这里插入图片描述

过渡版
s1 = s2
思路:打工人是拷贝构造,利用拷贝构造构造出的tmp,再让tmp和this交换,s1也就换好了,精华的点是:拷贝好了tmp出了作用域还会自动调用析构函数帮助我们把s1原来的空间释放掉

这里面不能直接用库里面的swap()交换两个对象,会造成栈溢出,原因是swap里面也是赋值又继续调用赋值重载又swap…
在这里插入图片描述

string& operator=(const string& s)
		{
			if (this != &s)
			{
				string tmp(s);
				
				std::swap(_str, tmp._str);
				std::swap(_size, tmp._size);
				std::swap(_capacity, tmp._capacity);

				//swap(tmp, *this);//对象直接交换,交换里面还是赋值重载,就会造成栈溢出
			}
			return *this;
		}

swap()

string里面的是交换成员变量,与算法库 的swap()里面不同的是:库里面的swap是深拷贝,对比string里的swap效率低一些

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

现代写法最终版

对现代写法赋值重载进行调试时,如果发现调用拷贝构造不要惊慌,因为我们这是现代写法,拷贝构造是打工人,帮助我们完成交换和析构

思路:在形参中就调用拷贝构造,构造形参tmp,进行与s1的交换
注意形参不要写const,const导致形参无法被修改也就不能交换了
在这里插入图片描述

void swap(string& s)
		{
			std::swap(_str, s._str);
			std::swap(_size, s._size);
			std::swap(_capacity, s._capacity);
		}
string& operator=(string tmp)
		{
			//this->swap(tmp);
			swap(tmp);

			return *this;
		}

析构函数

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

const char* c_str() const C的接口

返回C字符串_str

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

size() 返回有效字符个数

size_t size() const
		{
			return _size;
		}

[]符号重载

char& operator[](size_t pos)//无法加const,需要读写修改成员变量
		{
			assert(pos < _size);
			return _str[pos];
		}
	//const对象版本  只读
const char& operator[](size_t pos) const
		{
			assert(pos < _size);
			return _str[pos];
		}

reserve()

作用:修改capacity,单纯改变空间
不具约束力的请求,编译器没有缩容

测试发现reserve只扩容,不缩容
测试代码

int main()
{
	string s1("hello linux");
	s1.reserve(100);//单纯改变空间

	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	s1.reserve(5);//不具约束力的请求,编译器没有缩容
	cout << s1[9] << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	//开空间+填值初始化
	//比size大的部分用'\0'填充
	s1.resize(105);
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;


	s1.resize(0);//即使是0 也没缩容
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	
	return 0;
}

模拟实现reserve

思路:如果需要的空间>capacity就扩容,开辟需要n个空间,将原有数据拷贝过去,释放原来的空间,更新成员变量
注意:为什么push_back里面判断过需要扩容,reserve里面还是会检查一下是不是n > _capacity呢?因为reserve()不止给push_back使用,还有可能单独就想修改_capacity,就需要进行判断

void reserve(size_t n)
		{//调整想要的有效字符的空间,可能缩容但不进行缩容
			if (n > _capacity)
			{
				cout << "reserve->" << n << endl;
				char* tmp = new char[n + 1];//n是有效个数,还需要加\0
				//strcpy(tmp, _str);
				memcpy(tmp, _str, _size + 1);
				delete[] _str;
				_str = tmp;
				_capacity = n;
			}
			
		}

void push_back(char c)

追加一个字符
思路:在字符串末尾追加一个字符,可以二倍扩容
注意事项:因为涉及2倍,原有空间如果是0就需要利用三目来规避此问题

void push_back(char c)
		{
			//二倍扩容
			if (_size == _capacity)
			{
				reserve(_capacity == 0 ? 4 : _capacity * 2);
			}
			_str[_size++] = c;
			_str[_size] = '\0';
		}

string& append(const char* s)

追加字符串
思路:不能二倍扩容,当len很大,len + _size有可能大于二倍扩
至少要开到len+_size
注意事项:reserve(len + _size);//这里不需要len+_size+1 因为reserve里面已经+1

string& append(const char* s)
	{
	//不能二倍扩容,当len很大,len + _size有可能大于二倍扩
	//至少要开到len+_size
	size_t len = strlen(s);
	if (len + _size > _capacity)
	{
		reserve(len + _size);//这里不需要len+_size+1 因为reserve里面已经+1
	}
	//strcpy(_str + _size, s);
	memcpy(_str + _size, s, len + 1);//多拷贝一个\0
	_size += len;
	
	return *this;
		}

string& operator+=(char c)

string& operator+=(const char* s)

string& operator+=(char c)
	{
		//this->push_back(c);
		//(*this).push_back(c);//两个写法等价
		push_back(c);
		return *this;
	}
	string& operator+=(const char* s)
	{
		//this->append(s);
		append(s);
		return *this;
	}

void insert(size_t pos, size_t n, char c)

插入n个字符
思路:n + _size <= _capacity就刚好够,不用扩容,不然就扩容到n+_size
之后就是挪动数据,把_size位置的数据往后挪动n个,才能在Pos(包括pos)往后的位置空出n个位置来插入
注意:挪动数据时,pos位置在中间都没事,如果Pos==0,那么因为size_t是无符号整形,end–导致size_t = 42亿几,判断条件就停不下来了
那么解决方法有三种
1.将end和pos都强转为int,pos如果不强转那么会导致int end算术转换为size_t,仍然会进入循环
2.设置npos = -1 这是设置静态变量npos那么如果end == -1就结束
3.把前一个数据挪动到后面去
思路图
while(end>pos+n)
_str[end] = _str[end-3]
end–
在这里插入图片描述

在这里插入图片描述

void insert(size_t pos, size_t n, char c)
		{
			assert(pos <= _size);
			//扩容
			if (n + _size > _capacity)
			{
				reserve(n + _size);
			}
			// 挪动数据
			/*int end = _size;
			while (end >= (int)pos)
			{
				_str[end + n] = _str[end];
				end--;
			}*/
			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + n] = _str[end];
				end--;
			}
			for (size_t i = 0; i < n; i++)
			{
				_str[pos + i] = c;
			}
			_size += n;
	
		}

void insert(size_t pos, const char* s)

插入字符串
思路:和插入n个字符没啥区别,都是挪动然后插入n个字符

void insert(size_t pos, const char* s)
		{
			assert(pos <= _size);

			size_t len = strlen(s);
			//扩容// 至少扩容到_size + len
			if (len + _size > _capacity)
			{
				reserve(len + _size);
			}

			size_t end = _size;
			while (end >= pos && end != npos)
			{
				_str[end + len] = _str[end];
				end--;
			}
			for (size_t i = 0; i < len; i++)
			{
				_str[pos + i] = s[i];
			}
			_size += len;

		}

void erase(size_t pos, size_t len = npos)

删除pos位置(包括Pos)后续的len个字符
思路图:
在这里插入图片描述
思路:从pos位置开始删除,如果pos+len >= _size or len == npos那么很简单,相当于直接从pos位置是\0截断,如果不是这种情况,那么就需要把end = pos + len 的位置移动到Pos位置,他们俩++即可。
注意:先判断len = = npos如果是这种情况,如果先判断pos + len 会溢出

void erase(size_t pos, size_t len = npos)
		{
			assert(pos <= _size);
			if (len == npos ||pos + len >= _size)
			{
				_str[pos] = '\0';
				_size = pos;
			}
			else
			{
				size_t end = pos + len;
				while (end <= _size)
				{
					/*_str[end - len] = _str[end];
					end++;*/
					_str[pos++] = _str[end++];
				}
			}
			_size -= len;
		}

size_t find(const char* str, size_t pos = 0)

查找字符串子串,返回子串在主串中第一次出现的下标
注意:指针-指针,p2指向的位置不计算在内,而是p1和p2之间的元素个数,以下标版本的也一样
在这里插入图片描述

在这里插入图片描述

size_t find(const char* str, size_t pos = 0)
		{
			assert(pos < _size);

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

string substr(size_t pos = 0, size_t len = npos)

返回主串中的子串,返回从pos位置开始(包含pos)的len个字符
涉及小知识点:数组中下标也符合指针-指针,可以计算中间的元素个数,结合测试案例来看
小知识点思路图
在这里插入图片描述
测试代码:

void test_string4()
{
		ljh::string url1 = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";
		// 协议  域名  资源名
		size_t pos1 = url1.find("://");
		if (pos1 != ljh::string::npos)
		{
			ljh::string protocol = url1.substr(0, pos1);
			cout << protocol.c_str() << endl;
		}
		size_t pos2 = url1.find('/', pos1 + 3);
		if (pos2 != ljh::string::npos)
		{
			ljh::string domain = url1.substr(pos1 + 3, pos2 - (pos1 + 3));
			cout << domain.c_str() << endl;
			ljh::string uri = url1.substr(pos2 + 1);
			cout << uri.c_str() << endl;
		}
		//cout << "pos1 = " << pos1 << "pos2 = " << pos2 << endl;

}

模拟实现代码:
思路:如果要从pos位置开始提取的子串长度>=_size 那么就直接将n设置为_size-pos,也就是直接提取这一段,不然,就是不会超过_size,那么直接就开n个大小空间,利用循环并且控制好下标放到tmp里面就可以了

这里面pos + len 其实等价于 比如 pos +1 pos+1 - pos = 1这样其实算的是从pos位置开始计算的个数,也就是_size-pos,和上面的小知识点一样

string substr(size_t pos = 0, size_t len = npos)
		{
			assert(pos < _size);
			size_t n = len;
			if (len == npos || pos + len >= _size)
			{
				n = _size - pos;
			}
			string tmp;
			tmp.reserve(n);
			for (size_t i = pos; i < pos + n; i++)
			{
				tmp += _str[i];
			}
			return tmp;//返回值是自定义类型,必须调用拷贝构造进行深拷贝
		}

iterator迭代器 左闭右开[ )

在string类域里是一个char*指针类型重命名
typedef 这里的规律是 原本的类型放在中间,自定义名字放在右边,要加分号,宏才不加分号

定义

这里实现了两个版本,一个是读写 ,另一个是const对象的只读
end指针 return _str+_size 保证了end是开区间,指向最后一个的下一个

public:
		typedef char* iterator;
		typedef const char* const_iterator;

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

使用

细节:只要支持了迭代器,编译器自动支持范围for
并且使用迭代器时用auto自动推导(迭代器类型有点长)

ljh::string::iterator it = s1.begin();
	while (it < s1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;

	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;

	const ljh::string s3("hello world");
	s3[0];
	//ljh::string::const_iterator it1 = s3.begin();
	auto it1 = s3.begin();

	while (it1 < s3.end())
	{
		cout << *it1;
		it1++;
	}
	cout << endl;

void resize(size_t n, char c = ‘\0’)

函数说明:调整_size的大小,多了用指定字符填充,缩小和扩容都不会覆盖原有的数据
思路图
在这里插入图片描述

void resize(size_t n, char c = '\0')//默认用\0填充
		{
			if (n < _size)
			{
				_size = n;
				_str[n] = '\0';
			}
			else//等于不进入循环,相当于什么也没做
			{
				reserve(n);
				for (size_t i = _size; i < n ; i++)
				{
					_str[i] = c;
				}
				_str[n] = '\0';
				_size = n;
			}
		}

ostream& operator<<(ostream& out, const string& s)

//此函数放到ljh域外面,导致string优先使用库里的,在传参时会报错
	//所以放到域内,也可以给s加上ljh::
	ostream& operator<<(ostream& out, const string& s)
	{
		//可以不用友元,因为可以用[]访问
		/*for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}*/
		for (auto ch : s)
		{
			out << ch;
		}
		return out;
	}

istream& operator>>(istream& in, string& s)

istream& operator>>(istream& in, string& s)
	{
		s.clear();//库里面每次输入都清空s
		char ch = in.get();//get()每次读取一个字符,不管是啥
		//in >> ch;//in >> 阻塞性接口,in本身拿不到空格和换行
		while (ch != ' ' && ch != '\n')//去掉ch != ' '就类似于getline()
		{
			s += ch;
			ch = in.get();
		}
		return in;
	}

另一种写法,防止多次扩容效率低

//另一种写法,防止多次扩容效率低
	istream& operator>>(istream& in, string& s)
	{
		s.clear();//库里面每次输入都清空s

		//清除输入字符串开头的空格或者换行
		char ch = in.get();
		while(ch == ' ' || ch == '\n')
		{
			ch = in.get();
		}

		char buff[128];//buff相当于一个桶,满了就给s
		int i = 0;
		
		while (ch != ' ' && ch != '\n')
		{
			buff[i++] = ch;
			if (i == 127)//此时满了,127位置放\0,+=给s,如果还有就继续
			{
				buff[i] = '\0';
				s += buff;
				i = 0;//令 i= 0,如果还有就继续
			}
			ch = in.get();
		}
		//遇到空格或者换行就结束
		if (i != 0)
		{
			buff[i] = '\0';//i++后遇到空格或者换行,那么i位置不一定是\0,手动加上
			s += buff;
		}
		return in;
	}

void clear()

说明:删除字符串的内容,字符串将变成空字符串(长度为0个字符)。

即使stirng对象是空,string认为也是要有一个\0的

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

bool operator<(const string& s) const

思路:根据ASCIL码表值来比较,两个串先比较较少_size个数,如果有大或者小就返回,都比完了说明他们小的部分相同,再根据下面三种情况,只需要判断只有当_size<s._size时才会小于返回1
//hello hello false
//helloxx hello false
//hello helloxx true
//只有当_size<s._size时才会小于

bool operator<(const string& s) const
{
	size_t n = min(s._size, _size);
	for (size_t i = 0; i < n; i++)//先比较较小的_size次 如果没有返回在比较下面三种情况
	{
		if (_str[i] > s[i])
			return false;
		else if (_str[i] < s[i])
			return true;
	}
	//return _str[n] - s._str[n] < 0; 此方法不对 \0不应该参与比较
	
	//hello hello  false
	//helloxx hello false
	//hello helloxx true
	//只有当_size<s._size时才会小于
	
	return _size < s._size;	
}

其他写法

在这里插入图片描述

bool operator==(const string& s) const

bool operator==(const string& s) const
{
	return  _size == s._size && memcmp(_str, s._str, _size) == 0;
}

写出了 < 和 == ,其他的运算符比较(<=, >=, > ,!=)都可以根据<和= =进行取反互补进行判断出来
需要注意的是 比如 <= 返回的操作符是|| ,因为一个数不可能即等于又小于
return *this < s || *this == s;// < or == 皆可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值