c++学习笔记 类运算符重载与友元与类型转换

本文介绍了C++中如何通过运算符重载实现类对象的赋值、加法操作,以及如何利用友元解决特定运算符的使用问题。同时,讨论了类型转换在类中的应用,包括自定义转换函数以实现更灵活的赋值方式。
摘要由CSDN通过智能技术生成

有这样一个类:

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,所以会进行类型转换。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值