c++使用类

运算符重载

运算符函数的格式如下:

operator op(argument-list)

假设有一个类,为他定义了一个operator+()成员函数,以重载+运算符,以便能将两个类的对象相加,如果district2、sid和、sare都是类的对象,则可以这样编写:

district2=sid+sara;

编译器发现操作数都是类对象,因此使用相应的运算符函数替换上述运算符:

district2=sid.operator+(sara);

计算时间:一个运算符重载示例

2小时35分钟和2小时40分钟相加与内置类型不匹配。下面我们将定义一个使用方法来处理加法的Time类。
头文件

#ifndef MYTIME_H_
#define MYTIME_H_
class Time
{
private:
	int hours;
	int minutes;
public:
	Time();
	Time(int h, int m = 0);
	void AddMin(int m);
	void AddHr(int h);
	void Reset(int h = 0, int m = 0);
	Time Sum(const Time& t)const;
	void Show()const;
};
#endif
#include"mytime.h"
#include<iostream>
Time::Time()
{
	hours = minutes = 0;
}
Time::Time(int h, int m)
{
	hours = h;
	minutes = m;
}
void Time::AddHr(int h)
{
	hours += h;
}
void Time::AddMin(int m)
{
	hours += (m + minutes) / 60;
	minutes = (m + minutes) % 60;
}
void Time::Reset(int h , int m )
{
	hours = h;
	minutes = m;
}
void Time::Show()const
{
	std::cout << hours << " hours, " << minutes << " minutes";
}
Time Time::Sum(const Time& t)const
{
	Time sum;
	sum.minutes = minutes + t.minutes;
	sum.hours = hours + t.hours + sum.minutes / 60;
	sum.minutes %= 60;
	return sum;
}
#include<iostream>
#include"mytime.h"
int main()
{
	using std::cout;
	using std::endl;
	Time planning;
	Time coding(2, 40);
	Time fixing(5, 55);
	Time total;
	cout << "planning time = ";
	planning.Show();
	cout << endl;
	cout << "coding time = ";
	coding.Show();
	cout << endl;
	total = coding.Sum(fixing);
	cout << "coding.sum(fixing) = ";
	total.Show();
	cout << endl;
}

在这里插入图片描述
下面我们对程序进行修改,将Time类转换为重载的加法运算符。

#ifndef MYTIME_H_
#define MYTIME_H_
class Time
{
private:
	int hours;
	int minutes;
public:
	Time();
	Time(int h, int m = 0);
	void AddMin(int m);
	void AddHr(int h);
	void Reset(int h = 0, int m = 0);
	Time operator+(const Time& t)const;
	void Show()const;
};
#endif
#include"mytime.h"
#include<iostream>
Time::Time()
{
	hours = minutes = 0;
}
Time::Time(int h, int m)
{
	hours = h;
	minutes = m;
}
void Time::AddHr(int h)
{
	hours += h;
}
void Time::AddMin(int m)
{
	hours += (m + minutes) / 60;
	minutes = (m + minutes) % 60;
}
void Time::Reset(int h , int m )
{
	hours = h;
	minutes = m;
}
void Time::Show()const
{
	std::cout << hours << " hours, " << minutes << " minutes";
}
Time Time::operator+(const Time& t)const
{
	Time sum;
	sum.minutes = minutes + t.minutes;
	sum.hours = hours + t.hours + sum.minutes / 60;
	sum.minutes %= 60;
	return sum;
}
#include<iostream>
#include"mytime.h"
int main()
{
	using std::cout;
	using std::endl;
	Time planning;
	Time coding(2, 40);
	Time fixing(5, 55);
	Time total;
	cout << "planning time = ";
	planning.Show();
	cout << endl;
	cout << "coding time = ";
	coding.Show();
	cout << endl;
	total = coding + fixing;
	cout << "coding.sum(fixing) = ";
	total.Show();
	cout << endl;
	Time morefixing(3, 28);
	cout << "more fixing time = ";
	morefixing.Show();
	cout << endl;
	total = morefixing.operator+(total);
	cout << "morefixing.operator+(total) = ";
	total.Show();
	cout << endl;
}

在这里插入图片描述
假如t1 t2 t3都是Time对象,这样做可以吗:

t4=t1+t2+t3;

将这个语句转化为函数调用首先为:

t4=t1.operator+(t2+t3);

然后,函数参数本身被转化成一个函数调用,结果如下:

t4=operator+(t2.operator+(t3));

总之返回值是t1,t2,t3的∑,这是我们所期待的。

重载限制

  • 重载运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符。因此不能将-重载为计算两个double的∑,而不是他们的差。
  • 使用运算符时不能违反运算符原来的句法规则。例如不能将%重载成使用一个操作数,
  • 不能修改运算符的优先级。
  • 不能创建新运算符。例如,不能定义operator**()函数来表示求幂。
  • 不能重载以下运算符
  • sizeof
  • .
  • .*
  • ::
  • ?:
    详细可以看cpplus p314

友元

友元函数

前面我们重载运算符+可以实现两个类的相加,对于将*重载,将Time对象乘以实数,乘法运算符将一个Time值和一个double值结合在一起。下面的语句

Time Time::operator*(double mult)const
{
}
A=B*2.75

将被转换为

A=B.operator*(2.75)

但是下面的语句呢

A=2.75*B

记住左侧的操作数应是调用对象,因此编译器不能使用成员函数来替换该表达式。
然而还有一种解决方式–非成员函数,非成员函数不是由对象调用的,它使用的所有值都是显式参数。

Time operator*(double m,const Time&t);
A=2.75*B

与下面的非成员函数调用匹配

A=opertaor*(2.75,B);

但是非成员函数不能直接访问类的私有数据,至少常规非成员函数不能访问。然而,有一类特殊的非成员函数可以访问类的私有成员,他们被称为友元函数。

创建友元

创建友元函数的第一步是将其原型放在类声明中,并在原型声明前加上关键字friend:

friend Time operator*(double m,const Time & t);

该原型意味着下面两点:

  • 虽然operator*()是在类声明中声明的,但他不是成员函数,因此不能使用成员运算符来调用。
  • 虽然operator*()函数不是成员函数,但是它与成员函数的访问权限相同。
    下面是定义,不要再定义中使用friend,也不要用Time::限定符。
    例如:
Time operator *(double m, const Time& t)
{

}

这将被转化为:

A=operator*(2.75.B)

常用的友元:重载<<运算符

要让cout识别Time对象,一种方法是将一个新的函数运算符定义添加到ostream类声明中。但修改iostream文件是一个危险的注意,这样做会在标准接口上浪费时间。相反,通过Time类声明让Time类知道如何使用cout.
要使Time类知道使用cout,必须使用友元函数。因为下面的语句使用两个对象,其中一个是osream类对象(cout):

cout<<trip;

如果使用Time成员函数来重载<<,Time对象僵尸第一个操作数,就像使用成员函数重载*运算符那样。这意味着必须这样使用<<:

tirp<<cout;

这样会令人迷惑,但是可以通过友元函数,可以像下面这样重载运算符:

void operator<<(ostream & os

例如

class Time
{
private:
	int hours, minutes;
public:
	Time()
	{

	}
	Time(int h, int m)
	{
		hours = h;
		minutes = m;
	}
	friend void operator<<(ostream& os, const Time& t);
	friend Time operator*(double m, Time& t);
};
Time operator*(double m, Time& t)
{
	Time result;
	result.hours = 1;
	return result;
}
void operator<<(ostream& os, const Time& t)
{
	os << t.hours << " hours " << t.minutes << " minutes";
}
int main()
{
	Time a(3, 25);
	cout << a;
	
}

调用cout<<trip应使用cout对象本身,而不是它的拷贝,因此该函数按引用来传递该对象。

<<的第二种重载版本

上面的实现存在一个问题,像下面的语句无法工作:

cout<<"Trip time: "<<trip<<" (Tuesday)\n"; //can't do

对于一般的cout为什么能这样例如:

int x=5;
int y=8;
cout<<x<<y;

它等同于:

(cout<<x)<<y;

因此ostream类将operator<<()函数实现返回一个指向ostream对象的引用。具体地说,它返回一个指向调用对象(这里是cout)的引用。因此表达式(cout<<x)本身就是ostream对象cout,从而可以位于<<运算符的左侧。
因此可以对友元函数采用相同的方法。例如:

ostream& operator<<(ostream& os, const Time& t)
{
	os << t.hours << " hours " << t.minutes << " minutes";
	return os;
}

重载运算符:作为成员函数还是非成员函数

例如:

Time operator+(const Time&t)const;

这个类也可以使用下面的原型:

friend Time operator+(const Time & t1,const Time &t2);

但对于下面语句

T1=T2+T3

T1,T3,T2都是类对象。也就是说,编译器将转化为下面两个的任何一个:

T1=T2.operator+(T3);
T1=operator+(T2,T3);

但是在定义运算符时,必须选择其中一种格式,否则会产生二义性,导致编译错误。那么哪种格式最好呢?对于某些运算符来说,成员函数时唯一合法的选择。在其他情况下,这两种格式没有太大的区别。有时,根据类设计,使用非成员函数版本可能更好。

一个矢量类

可以用枚举RECT来表示直角坐标模式(默认值)POL表示极坐标模式,这样的成员被称为状态成员,因为这种成员描述的是对象所处的状态。例如:

#ifndef VECTOR_H_
#define VECTOR_H_
#include<iostream>
namespace VECTOR
{
	class Vector
	{
	public:
		enum Mode{RECT,POL};
	private:
		double x;
		double y;
		double mag;
		double ang;
		Mode mode;
		void set_mag();
		void set_and();
		void set_x();
		void set_y();
	public:
		Vector();
		Vector(double n1, double n2, Mode form = RECT);
		void reset(double n1, double n2, Mode form = RECT);
		~Vector();
		double xval()const
		{
			return x;
		}
		double yval()const
		{
			return y;
		}
		double magval()const
		{
			return mag;
		}
		double angval()const
		{
			return ang;
		}
		void polar_mode();
		void rect_mode();
		Vector operator+ (const Vector & b)const;
		Vector operator - (const Vector & b)const;
		Vector operator-()const;
		Vector operator*(double n)const;
		friend Vector operator*(double n, const Vector& a);
		friend std::iostream& operator<<(std::iostream& os, const Vector& v);
	};
}
#endif

enum Mode{RECT,POL};
Vector::Vector(double n1,double n2,Mode form)
{
	mode=form;
	if(form==RECT)
	{
	}
	else if(form==POL)
	{
	}
	else
	{
	}
}

为Vector类重载算数运算符

在使用x、y坐标时,将两个矢量相加很简单例如:

Vector Vector::operator+(const Vector& b)const
	{
		Vector sum;
		sum.x = x + b.x;
		sum.y = y + b.x;
		sum.set_ang();
		sum.set_mag();
		return sum;
	}

下面有更简洁的方法:

Vector Vector::operator+(const Vector&b)const
{
return Vector(x+b.x,y+b.y);
}

对已重载的运算符进行重载

在c++中,-运算符有两种含义,只有一个操作数时(-x),它是负号运算符,减法运算符是有两个操作数的。
要从矢量A减去矢量B例如:

Vector operator-(const Vector &b)const
{
return Vector(x-b.x,y-b.y);
}

接下来看一元负号运算符

Vector operator-()const
{
return vector(-x,-y);
}

但对于只有二元形式的运算符(如除法运算符)只能将其重载为二元运算符。

随机数

rand()函数返回从0到某个值之间的随机整数,rand()获得的随机数是伪随机数,通常使用srand(time(0))来搭配使用。
头文件cstdlib包含了srand()和rand()的原型,ctime包含了time的原型。c++11使用头文件radom中的函数提供了更强大的随机数支持。

类的自动转换和强制类型转换

例如:

class Stonewt
{
private:
	int a;
	int b;
public:
	Stonewt()
	{

	}
	Stonewt(int d, int c=0)
	{
		a = d;
		b = c;
	}
};
int main()
{
	Stonewt one;
	one = 3;
}

程序可以使用Stonewet(int)来创建一个临时的Stonewt对象,并将3作为初始化值。然后采用逐成员赋值方式将该临时对象的内容复制到one中。,这一过程叫做隐式转换,因为它是自动进行的。
只有接受一个参数的构造函数才能作为转换函数。c++提供了关键字explicit用于关闭这种特性。也就是说,可以这样声明构造函数:

explicit Stonewt(int a);

但是还是可以允许显式的类型转换例如:

Stonewt myCat;
myCat=Stonewt(19);

转换函数

转换函数的形式为:

operator typeName();

需要注意以下几点:

  • 转换函数必须是类方法;
  • 转换函数不能指定返回类型;
  • 转换函数不能有参数;

例如转化为double类型的函数原型如下:

operator double();

例如:

Stonewt::operator int()const
{
return int(pounds+0.5);
}

假设有以下代码:

int ar[20];
Stonewt temp(14,4);
int Tmep=1;
cout<<ar[temp];

通常编译器会报错,但是由于定义了转换函数,temp将被转化为int从而避免报错,所以说,原则上最好使用显式转换,而避免隐式转换。c++11中,可以将函数弄成显式的例如:

class Stonewt
{
explicit operator int()const;
explicit operator double()const;
};

另一种方法是用一个功能相同的非转换函数替换该函数即可。例如:

int Stonewt::Stone_to_int();

下面语句是非法的:

int a=poppins;

但是可以这样:

int a=Poppins.stone_to_int();

转换函数和友元函数

对于两个对象实现加法可以有:

Stonewt Stonewt::operator+(const Stonewt& st)const
{
	double pds = pounds + st.pounds;
	Stonewt sum(pds);
	return sum;
 }
Stonewt operator+(const Stonewt& st1, const Stonewt& st2)
{
	double pds = st1.pounds + st2.pounds;
	stonewt sum(pds);
	return sum;
}

如果提供了Stonewt(double)函数,则可以这样

Stonewt a(1,12);
double b=2.0;
Stonewt total=a+b;

对于友元函数可以:

Stonewt c(1,23);
double d=2.0;
Stonewt total=d+c;

但是如果定义了operator double()成员函数,将造成混乱,因为编译器可以将对象转化成double类型从而实现两个double的加法。导致二义性。
对于

total=b+d;

被转化为

total=operator+(b,d);

这两个参数都是double类型,因此将调用构造函数Stonewt(double),但是不能调用成员函数来相加,这没有意义。c++不会试图将b转化成Stonewt对象因此定义为友元更好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值