有这样一个类:
class Str
{
char str[100];
int n;
public:
Str(const char *pstr = "\0"){
strcpy(str, pstr);
n = strlen(str);
}
const char *getstr()const{ return str; }
const int getcount()const{ return n; }
void add(char *pstr){
if ((strlen(str) + strlen(pstr)) < 100)
{
strcat(str, pstr);
n = strlen(str);
}
else
cout << "size error!\n";
}
void show()const{ cout << str; }
~Str(){}
};
这个类仿string类(劣质),允许用户将字符串相加(有限制),并且计算字符个数。
但是我们更想用类似内置类型那样的运算方法:
int a=1,b=3;
a=a+b;
而不是经过对象的方法调用来实现:
Str s1("Hello ");
s1.add("words!");//不想这样使用
s1.show();//想用cout<<s1;来输出
比如说我们想用下面的方法不是调用类方法(add)来进行字符串相加:
Str s2("c++!");
s1=s1+s2;
这就需要重载运算符“+”。
返回值 operator符号(参数列表);
我们先来重载运算符“=”使之能够进行两个对象的赋值操作,现在看看是怎么使用的:
class Str
{
...
public:
...
//之前的代码
Str &operator=(const Str &s)
{
strcpy(str, s.str);
n = s.n;
return *this;
}
};
这样就可以用s1=s2;这样的表达式了。
如何工作的?
其实是相当于调用了s1.operator=(s2);
为什么需要返回一个*this?
*this是当前这个对象,如果是void operator=(const Str &s);那就使能s1=s2;而不能连续赋值了,
比如Str s3; s3=s1=s2;对应:s3.operator=(s1.operator=(s2));这样会出错,因为operator=()方法的返回值如果是void,那s3.operator=()就没有实参了。
接着我们重载运算符“+” :
class Str
{
...
public:
...
//之前的代码
Str operator+(const Str &s)
{
if ((strlen(str) + strlen(s.str)) < 100)
{
Str temp;
strcpy(temp.str, str);
strcat(temp.str, s.str);
temp.n = n + s.n;
return temp;
}
else
cout << "size error!\n";
}
};
在重载运算符“+”和“=”后,Str类就可以这样运算:
s3=s1+s2;
相当于s3.operator=(s1.operator+(s2));
因为s1.operator+(s2)会返回一个临时变量temp,所以s3.operator=()的参数不会为空。
运算符重载也是有限制的。
有些特别意义的符号无法重载,比如:?双目运算符、.成员运算符、::作用域运算符、*指针运算符、#预处理命令等,这些都不可以重载,还有一些只可以是友元函数才能重载。
可以重载的表可以自己搜索。
但是必须要使用对象与对象才能进行运算这有点麻烦,可否直接s3=s2+"primer";?
答案是可以的,但是必须定义一个应对c指针的重载运算符"+":
class Str
{
...
public:
...
//之前的代码
Str operator+(const char *pstr)
{
if ((strlen(str) + strlen(pstr)) < 100)
{
Str temp;
strcpy(temp.str, pstr);
strcat(temp.str, pstr);
temp.n = n + strlen(pstr);
return temp;
}
else
cout << "size error!\n";
}
};
当遇到语句s3=s2+"primer";时,将被转换为s3.operator=(s2.operator+("primer"));
挺不错的,但是遇到这种情况该如何:
s3="primer"+s2;
对于表达式c=a+b和c=b+a是没有区别的,但是在这里"primer"不是Str类的对象,它没有operator+()这个方法,因此编译器会报错,不能使用"primer".operator+(s2)来替换该表达式。
也许你会突发奇想,定义一个这样的运算符:Str operator+(const char *pstr,const Str &s);
这是非法的,因为因为不知道是哪个对象调用,s3="primer"+s2;编译器替换后就是s3.operator=("primer"+s2);然而s3的方法并不接受这样的参数。
有一种比较好的方法,就是使用友元
firned 返回值 函数名(参数列表);
当一个成员被声明为友元时,该成员不再属于类,但是却和这个类的成员一样具有访问权限。
class Str
{
...
public:
...;
//之前的代码
friend Str operator+(const char *pstr, const Str &s) //friend关键字
{
if ((strlen(pstr) + strlen(s.str)) < 100)
{
Str temp;
strcpy(temp.str, pstr);
strcat(temp.str, s.str);
temp.n = strlen(pstr) + s.n;
return temp;
}
else
cout << "size error!\n";
}
};
有了这个特殊的重载运算符后,就可以使用s3="primer"+s2;,对应调用s3.operator=(operator("primer",s2));
相当于定义了一个叫operator+的函数一样,只是编译器会把char *+Str自动转换为operator("primer",s2);
但是对于有强迫症的我来说,使用.show()方法来输出内容而不是cout显得有点难受,能不能使用运算符重载让编译器检查到cout<<s3;的时候进行替换呢?
你很有想法,和我学 咳咳...
重载运算符<<的确可以做到这个效果。
cout是ostream类的对象,而s3是Str类的对象,也就是说cout.operator<<(s3)的重载运算符是这样:
operator<<(ostream &os,const Str &s);
于是先来实现这个方法:
class Str
{
...
public:
...
//之前的代码
friend ostream &operator<<(ostream &os, const Str &s)
{
os << s.str;
return os;
}
};
这里为什么需要返回一个ostream对象的引用?
当我们进行简单的输出s3对象:cout<<s3; 没问题,正常工作。
但是我们想象通常的cout一样连续输出:cout<<"s3:"<<s3<<s3.getcount()<<" size\n";这样就会出错。
一步一步来,先看看那cout<<"s3",相当于cout.operator<<("s3");,但是iostream里面有定义它会返回一个ostream对象,所以它的返回值是ostream对象,因此可以是
cout.operator<<("s3").operator<<(s3); 它返回一个ostream对象,然后这个ostream对象再调用.operator<<(s3)进行输出。
然后你会发现如果Str类的友元函数
operator<<(ostream &os, const Str &s)
如果没有返回值,那么后面的<<s3.getcount()<<" size\n"就会出错,因为它是void.operator<<(...)。
如果要输出s3的大小,那就必须哟调用该对象的getcount()方法,但是这样太不美观,而且不方便,
比如我想把大小赋给一个int变量a:a=s3.setcount();
能不能做到这样获取呢:a=s3;
没问题,但是这样必须定义一个转换函数
operator 类型();
转换函数没有返回值与参数
class Str
{
...
public:
...;
//之前的代码
operator int()
{
return n;
}
};
a=s3;
当编译器发现右边是Str,而左边是int时,就会检查是否定义了与此匹配的转换函数,并使用。然而并非一定是int型,比如是double也会转,因为int可以提升转换为double,所以会进行类型转换。