北京大学程序设计与算法(三):第05周测验001:全面的MyString

“全面的MyString”题解及总结

题目要求

总时间限制: 1000ms 内存限制: 65536kB
描述:程序填空,输出指定结果,空间有限,题目代码就不附录了。
整个程序是想让你不使用cstring库,按照学过的构造函数、重载等知识编写一个string类,实现对字符串对象的直接操作。

作答分析

首先需要将main函数大概看懂,在main函数中实现的功能分别为:构造函数、复制构造函数的编写、运算符<<、+、=、+=、[]、()等的重载、深拷贝等内容,同时也可以知道某些运算符需要多次重载,比如+,由于双目运算符+的前后两个参数共有3中排列方式:字符串+对象、对象+字符串、对象+对象,因此需要多次重载。

实现代码

#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;

/*
int strlen(const char * s)  //限制大家只能使用提供的这几个函数
{                         //对于cstring库提供的其他函数无法使用  //但测试时发现这几个函数无法使用
	int i = 0;
	for( ; s[i] ;++i );
	return i;
}
void strcpy(char * d,const char *s)
{
	int i = 0;
	for( i=0; s[i] ;++i )
		d[i] = s[i];
	d[i] = 0;
}
int strcmp(const char *s1,const char *s2)
{
	for( int i=0; s1[i] && s2[i] ;++i )
	{
		if(s1[i] < s2[i])
			return -1;
		else if(s1[i] > s2[i])
			return 1;
	}
	return 0;
}
void strcat(char *d,const char *s)
{
	int len = strlen(d);
	strcpy(d+len,s);
}
*/

class Mystring
{
	//补充你的代码
private:
	char *str;
public:
	Mystring():str(NULL){}  //默认构造函数
	Mystring(const char* s)  //带参构造函数
	{
		if(s)  //如果str内容不为0时
		{
			str = new char[strlen(s)+1];
			strcpy(str,s);
		}
		else
			str = NULL;
	}
	Mystring(const Mystring & my)  //复制构造函数
	{
		if(my.str)
		{
			str = new char[strlen(my.str)+1];
			strcpy(str,my.str);
		}
		else
			str = NULL;
	}
	~Mystring()  //析构函数
	{
		if(str)
			delete [] str;
	}
	friend ostream & operator<<(ostream &o,Mystring & my)
	{
		if(my.str)  //必须有这句话,否则会产生错误
			o << my.str;
		return o;
	}
	Mystring & operator=(const Mystring & my)  //执行深拷贝,达到s1 = s2的目标
	{
		if(str == my.str)
			return *this;
		if(str)
			delete [] str;
		if(my.str)  //如果my.str不为NULL才执行复制操作
		{
			str = new char[strlen(my.str)+1];
			strcpy(str,my.str);
		}
		else
			str = NULL;
		return *this;
	}

	Mystring & operator=(const char *my)  //达到s1 = "**"的目标
	{
		if(str)
			delete [] str;
		if(my)  //如果my.str不为NULL才执行复制操作
		{
			str = new char[strlen(my)+1];
			strcpy(str,my);
		}
		else
			str = NULL;
		return *this;
	}

	  //版本1
	friend Mystring operator +(const Mystring & a,const Mystring & b)
	{    //最好的做法!!直接使用两个对象的引用作为形参,可以对所有情况进行囊括:
	      //如果参数中有字符串,首先使用构造函数将字符串转换为一个临时对象,再使用复制构造函数进行初始化
		Mystring my;
	    my.str = new char[strlen(a.str)+strlen(b.str) + 1];
		strcpy(my.str,a.str);
        strcat(my.str,b.str);
		return my;
	}
	
	//    //版本2
	//friend Mystring operator+(Mystring &a, Mystring &b)  //要使用两个参数,就必须声明为友元函数
	//{
	//	char *temp = new char[strlen(a.str) + strlen(b.str)];
	//	strcpy(temp, a.str);
	//	strcat(temp, b.str);
	//	return Mystring(temp);  //调用构造函数进行初始化
	//}
	//friend Mystring operator+(const char * s, Mystring &my )
	//{
	//	char *temp = new char[strlen(s) + strlen(my.str)+1];
	//	strcpy(temp,my.str);
	//	strcat(temp, my.str);
	//	return Mystring(temp);
	//}
	//friend Mystring operator+(Mystring &my, const char *s)
	//{
	//	char *temp = new char[strlen(my.str) + strlen(s) + 1];
	//	strcpy(temp, my.str);
	//	strcat(temp, s);
	//	return Mystring(temp);
	//}

    //       //版本3
	//Mystring operator+(Mystring &my)
	//{
	//	char *temp = new char[strlen(str) + strlen(my.str)];
	//	strcpy(temp, str);
	//	strcat(temp, my.str);
	//	return Mystring(temp);  //调用构造函数进行初始化
	//}
	//friend Mystring operator+(const char * s, Mystring &my )  //此处必须使用友元函数,才可以用两个参数
	//{
	//	char *temp = new char[strlen(s) + strlen(my.str)+1];
	//	strcpy(temp,my.str);
	//	strcat(temp, my.str);
	//	return Mystring(temp);
	//}
	//Mystring operator+(const char *s)
	//{
	//	char *temp = new char[strlen(str) + strlen(s) + 1];
	//	strcpy(temp, str);
	//	strcat(temp, s);
	//	return Mystring(temp);
	//}

    Mystring & operator +=(const Mystring &a)
	{  //将对象作为参数,而不是字符串,扩大了适用范围
		//字符串作为实参,首先调用构造函数对形参(对象)进行初始化
	    char *s = new char[strlen(str)+1];
	    strcpy(s,str);  //将原本的字符串保存在新开辟的空间s中
	    delete [] str;
	    str=new char[strlen(s)+strlen(a.str)+1];
        strcpy(str,s);
		delete [] s;  //清除新开辟的空间
        strcat(str,a.str);
        return *this;
	}
	char & operator[](int i)  //重载[]
	{
		return str[i];  //返回值的类型不符合
	}
	char * operator()(int order,int len)  //重载()
	{  //提取从下标order开始长度为len的子串
		char* mystr = new char[len];
		for(int i=0;i<len;i++)
			mystr[i] = str[i+order];                      //正确的()重载
			mystr[len] = '\0';  //为字符串最后添加结束符
			return mystr;
	}
	bool operator==(const Mystring & my)
	{
		int len_left = strlen(str);
		int len_right = strlen(my.str);
		if(len_left == len_right)
		{
			for(int i;i<len_right;i++)
			{
				if(str[i] != my.str[i])
				{return false;}  // return,就意味着该函数调用结束
			}
			return true;
		}
		else
			return false;
	}
	bool operator>(const Mystring & my)
	{
		for( int i=0;str[i] && my.str[i];++i )
		{
			if(str[i] < my.str[i])
				return false;
			else
				continue;
		}
		return true;
	}
		bool operator<(const Mystring & my)
	{
		for( int i=0;str[i] && my.str[i];++i )
		{
			if(str[i] > my.str[i])
				return false;
			else
				continue;
		}
		return true;
	}
};
int CompareString( const void *e1,const void *e2 )  //使用qsort函数必须编写的函数
{
	Mystring *s1 = (Mystring *)e1;  //强制类型转换
	Mystring *s2 = (Mystring *)e2;
	if( *s1 < *s2 )
		return -1;
	else if( *s1 == *s2 )
		return 0;
	else if( *s1 > *s2 )
		return 1;
}
int main()
{
	Mystring s1("abcd-"),s2,s3("efgh-"),s4(s1);  //构造函数&复制构造函数
	Mystring sArray[4] = {"big","me","about","take"};
	cout << "1. "<<s1<<s2<<s3<<s4<<endl;  //运算符<<重载
	s4 = s3;  //深拷贝  
	s3 = s1+s3;  //运算符重载+、=
	cout << "2. "<<s1<<endl;
	cout << "3. "<<s2<<endl;
	cout << "4. "<<s3<<endl;
	cout << "5. "<<s4<<endl;
	cout << "6. "<<s1[2]<<endl;
	s2 = s1;
	s1 = "ijkl-";  //运算符=重载
	s1[2] = 'A';  //运算符[]重载
	cout << "7. " << s2 << endl;
	cout << "8. " << s1 << endl;
	s1 += "mnop";  //运算符重载+=,实现字符串拼接功能
	cout << "9. " << s1 << endl;
	s4 = "qrst-" + s2;  //对+的再重载
	cout << "10. " << s4 << endl;
	s1 = s2 + s4 + " uvw " + "xyz";
	//s1 = "abcd-efgh-ijkl-xyz";
	cout << "11. " << s1 << endl;
	qsort(sArray,4,sizeof(Mystring),CompareString);  //以升序排序 ptr 所指向的给定数组
	for( int i = 0;i < 4;i ++ )
	cout << sArray[i] << endl;  //升序排列,先看首字母a最小,z最大
	//s1的从下标0开始长度为4的子串ni
	cout << s1(0,4) << endl;  //对()进行重载
	//s1的从下标5开始长度为10的子串
	cout << s1(5,10) << endl;
	system("pause");
	return 0;
}

程序的运行结果如下:
在这里插入图片描述

总结

这个程序在调试的时候遇到了诸多麻烦,其中最让人费解的是,老师提供的这几个字符串函数编译时居然会报错,最终我没有使用题目中提供的这几个函数,而是使用了cstring中自带的函数。还有就是对于字符串的初始化问题,如果字符串初始化不对,或者没有完全初始化,输出结果要么编译不通过,要么结果会出现”烫“之类的莫名奇怪的汉字。在本次例题中,我都是先使用new出来一片空间,再将原来的字符copy到新分配出的空间中,如下:

	    char *s = new char[strlen(str)+1];
	    strcpy(s,str);  //将原本的字符串保存在新开辟的空间s中

对于运算符+的重载方式,本程序提供了三种可行的方案,其中方案1最佳。方案1是将+前后的参数,不论是字符串还是对象,都作为const Mystring & 类型处理,这样即使是字符串,也会先使用构造函数转变为一个临时对象,在使用复制构造函数进行初始化,最终还是作为一个对象使用的,最为简洁。方案2和方案3的区别在于:方案2函数参数都是2个,因此必须都要声明为友元函数,作为全局函数处理;而方案3函数参数为1个,因此不需要声明为友元函数。

特别需要注意的是,对于运算符+的重载!+运算符的返回值不能够是引用,这点上面与=运算符不同,由于=运算符需要实现连续操作(例如:(a=b)=c),因此=运算符的返回值必须是引用,而+运算符的返回值不可以作左值,也就不可以当作引用使用。

有关于函数的返回”值“和返回”引用“引用,我想这两种方式对于初学者(比如我这样的菜鸡)确实较难理解,但网上有大佬是这样子解释的:1、通常的返回机制将返回值复制到临时存储区域中, 随后调用程序将访问该区域;而返回引用则程序则调用程序将直接访问返回值 2、引用返回左值 3、当返回值时,函数返回值时会产生一个临时变量作为函数返回值的副本,并调用复制构造函数将*this传给这个临时变量,并且赋值得到的是一个右值,右值是不能继续赋值的,而返回引用时,引用就是obj对象本身,赋值得到一个左值,是可以继续赋值的。
更为详细的链接大家可以参考:
1、https://blog.csdn.net/zhuzhaoming1994/article/details/80371779
2、https://blog.csdn.net/weixin_41413441/article/details/79180951

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值