学过C++中运算符重载概念后,做到北大郭炜老师的MyString这道题,由于涉及内容比较多,所以值得拿出来分析一下:
描述
补足MyString类,使程序输出指定结果
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MyString {
char * p;
public:
MyString(const char * s) {
if( s) {
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
~MyString() { if(p) delete [] p; }
// 在此处补充你的代码
};
int main()
{
char w1[200],w2[100];
while( cin >> w1 >> w2) {
MyString s1(w1),s2 = s1;
MyString s3(NULL);
s3.Copy(w1);
cout << s1 << "," << s2 << "," << s3 << endl;
s2 = w2;
s3 = s2;
s1 = s3;
cout << s1 << "," << s2 << "," << s3 << endl;
}
}
输入
多组数据,每组一行,是两个不带空格的字符串
输出
对每组数据,先输出一行,打印输入中的第一个字符串三次
然后再输出一行,打印输入中的第二个字符串三次
样例输入
abc def
123 456
样例输出
abc,abc,abc
def,def,def
123,123,123
456,456,456
下面我们来分析一下:
1.对于MyString s1(w1);MyString s3(NULL);这两条语句来说,显然调用了题中已给出的构造函数,无需我们操心,注意老师构造函数中的写法,先是判断一下w1是否为空字符串,如果是则直接另s1也为空即可,如果有内容再进行字符串间的赋值操作。这样可以节省不必要的时间开销,我们在写另外几个函数时也会用到同样的思想。
2.对于MyString s2 = s1;这条语句,则需要我们自己来写复制构造函数。
MyString(MyString& s)
{ if(s.p){
p=new char[strlen(s.p)+1];
strcpy(p,s.p);
}
else
p=NULL;
}
那么我们为什么要自己写复制构造函数呢?用系统默认的它不香吗?事实上,如果使用了系统自己生成的构造函数,那么s1与s2中的p指针将指向同一片内存地址,二者是绑在同一条绳上的蚂蚱,没有真正实现复制的功能。因此以后我们遇到成员变量中含有指针的类时,还是自己写复制构造函数为好。ps:自己动手,丰衣足食:)
3.而对于s3 = s2;和s1 = s3;这两条语句来说,则需要我们对“=”进行运算符重载(注意这两条语句与第2条的区别),左右两侧操作数均为MyString类。
MyString & operator=(const MyString & m)
{
if(p)
delete [] p;
if(m.p)
{
p=new char[strlen(m.p)+1];
strcpy(p,m.p);
}
else
p=NULL;
}
事实上,这里我们不进行重载编译也可以通过,因为C++本身是支持同类两个对象间用“=”赋值的,赋值的过程时将一个对象的数据成员在存储空间的状态赋值给另一个对象的数据成员的存储空间。但是由于我们这个类的数据成员p是动态内存分配的数据,粗暴的直接赋值可能出现严重后果,所以有必要自己重载一下。
4.对于s2 = w2;这条语句,也需要我们对“=”进行重,只不过这次左右两边操作数一个是MyString类,一个是char*字符串,这里不再赘述。
MyString& operator=(const char*s)
{
if(s)
{
if(p)
delete [] p;
p=new char[strlen(s)+1];
strcpy(p,s);
}
else
p=NULL;
return *this;
}
5.而对于s3.Copy(w1);这条语句,我们只要写出一个能实现字符串拷贝功能的Copy成员函数即可,这里不必多想。
void Copy(const char*s)
{
if(p)
delete [] p;
if(s){
p=new char[strlen(s)+1];
strcpy(p,s);
}
else
p=NULL;
6.接下来就是cout<<s1;等等这些用于输出的语句,这时我们需要对“<<”运算符进行重载。
friend ostream& operator<<(ostream& o,MyString& str)
{
o<<str.p;
return o;
}
需要注意的是对于流输入输出运算符的重载,需要把他们写为全局函数,如果要用到类的私有数据成员,则将其声明为类的友元即可。另外注意到返回值和第一个参数类型均为ostream&,这里用引用的目的是对于cout<<s1<<s2这种语句可以做到连续输出,如果返回的是ostream,则cout<<s1这条语句结束后将返回cout的临时变量,它不可以作为左值,因而错误。
下面贴出完整代码,供大家参考:
#include <iostream>
#include <string>
#include <cstring>
using namespace std;
class MyString
{
char * p;
public:
MyString(const char * s)
{
if( s)
{
p = new char[strlen(s) + 1];
strcpy(p,s);
}
else
p = NULL;
}
~MyString()
{
if(p)
delete [] p;
}
MyString(MyString& s)
{
if(s.p)
{
p=new char[strlen(s.p)+1];
strcpy(p,s.p);
}
else
p=NULL;
}
friend ostream& operator<<(ostream& o,MyString& str)
{
o<<str.p;
return o;
}
MyString& operator=(const char*s)
{
if(s)
{
if(p)
delete [] p;
p=new char[strlen(s)+1];
strcpy(p,s);
}
else
p=NULL;
return *this;
}
MyString & operator=(const MyString & m)
{
if(p)
delete [] p;
if(m.p)
{
p=new char[strlen(m.p)+1];
strcpy(p,m.p);
}
else
p=NULL;
}
void Copy(const char*s)
{
if(p)
delete [] p;
if(s)
{
p=new char[strlen(s)+1];
strcpy(p,s);
}
else
p=NULL;
}
};
int main()
{
char w1[200],w2[100];
while( cin >> w1 >> w2)
{
MyString s1(w1),s2 = s1;
MyString s3(NULL);
s3.Copy(w1);
cout << s1 << "," << s2 << "," << s3 << endl;
s2 = w2;
s3 = s2;
s1 = s3;
cout << s1 << "," << s2 << "," << s3 << endl;
}
}