题目要求
总时间限制: 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