C++ 运算符重载

运算符重载

Complex复数类

运算符重载的方法

运算符重载的方法是定义一个重载运算符的函数,使指定的运算符可以实现在函数中指定的新的功能。
重载运算符的函数一般格式如下:

	    函数类型  operator 运算符名  (形参列表)
	        {
		        对运算符重载的处理
	        }

函数名由 operator 和运算符组成。
例:

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	/*无参构造*/
	Complex(double a, double b);/*有参构造*/
	/*负号运算符重载*/
	Complex  operator - ();
};
/*负号运算符重载*/
Complex  Complex::operator - ()
{
	Complex temp;
	temp.real = -real;
	temp.imag = -imag;
	return temp;
}

直接调用重载的运算符

Complex Z1 { 1,2 };
Z2 = Z1.operator -(); // Z2 = -1 -2i

隐式调用重载的运算符

Complex Z{ 1,2 };
Z2 = -Z1; // Z2 = -1 -2i

运算符重载规则

  1. C++不允许用户自定义运算符,只能对已有的C++运算符进行重载。
  2. 不能重载的运算符:
    1. . (成员访问运算符)
    2. .* (成员指针访问运算符)
    3. : : (作用域限定符)
    4. sizeof (长度运算符)
    5. ?: (条件运算符)
  3. 重载无法改变运算符对象(即操作数)的个数。
  4. 重载无法改变运算的优先级。
  5. 重载无法改变运算的结合性。
  6. 重载运算符的函数不能有默认的参数。
  7. 重载运算符必须和用户自定义类型的对象一起使用,其参数至少应有一个是类对象(或类对象的引用)。
  8. 应当使重载运算符的功能类似于该运算作用于标准数据时所实现的功能。
  9. 通常情况下,不应该重载逗号、取地址、逻辑与和逻辑或运算符。

运算符重载函数作为类成员函数和友元函数

对运算符重载的函数有两种处理方式:

  1. 把运算符重载的函数作为类的成员函数。
    如果将运算符作为成员函数重载,它可以通过 this 指针自由的访问本类的数据成员,因此可以少写一个函数参数。
  2. 运算符重载的函数不是类的成员函数,在类中应把它声明为友元函数。
    如果将运算符作为友员函数重载,则必须写全函数参数。
    对于运算符重载的函数处理建议:
  1. C++中规定:赋值运算符=、下标运算符[]、函数调用运算符()、成员访问箭头运算符->必须作为成员函数重载。
  2. 流插入“<<”和流提取“>>”运算符、类型转换运算符函数不能定义为类成员函数,只能作友元函数。
  3. 复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
  4. 改变对象状态的运算符或者与给定类型密切相关的运算符(如自增、自减和解引用运算符)应作为成员函数重载。
  5. 具有对称性的运算符可能转换任意一端的运算对象(如算术、相等性、关系和位运算符)应作为为友元函数重载。

二元算术运算符重载

通常情况下,我们把算术和关系运算符定义成非成员函数以允许对左侧或右侧的运算对象进行转换。
因为这些运算符一般不需要改变运算对象的状态,所以形参都是 const 引用。
以加号运算符为例:

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	/*无参构造*/
	Complex(double a, double b);/*有参构造*/
	/*加号运算符重载*/
	friend Complex  operator + (const Complex& Z1 ,const Complex& Z2);
};
/*加号运算符重载*/
Complex operator + (const Complex& Z1, const Complex& Z2)
{
	Complex temp;
	temp.real = Z1.real + Z2.real;
	temp.imag =  Z1.imag + Z2.imag;
	return temp;
}

算术运算符通常会计算它的两个运算对象并得到一个新值,这个值有别于任意一个运算对象,常常位于一个局部变量之内,操作完成后返回该局部变量的副本作为其结果。

关系运算符重载

应当比较对象的每一个数据成员,只有当所有对应的成员都相等时才认为两个对象相等。

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	/*相等运算符重载*/
	friend bool operator == (const Complex& Z1,const Complex & Z2);
	/*不相等运算符重载*/ 
	friend bool operator != (const Complex& Z1,const Complex& Z2);
};
/*相等运算符重载*/
bool operator == (const Complex& Z1, const Complex& Z2)
{
	if (Z1.real == Z2.real && Z1.imag == Z2.imag)
		return true;
	else
		return false;
}
/*不相等运算符重载*/ 
bool operator != (const Complex& Z1, const Complex& Z2)
{
	if (Z1.real == Z2.real && Z1.imag == Z2.imag)
		return false;
	else
		return true;
}

另一个例子:比较两个盒子的大小。

#include <iostream>
using namespace std;

class Box {
private:
	int length;     //长度 
	int width;      //宽度 
	int height;     //高度
public:
    Box() = default;         
	Box(int H, int W, int L) : height{ H }, width{ W }, length{ L } {}
   int Volume() const //求盒子体积
   {
	   return length * width * height;
   }
  friend bool operator == (const Box& B1, const Box& B2)
  {
	  if (B1.Volume() == B2.Volume())
		  return true;
	  else
		  return false;
  }
  friend bool operator != (const Box& B1, const Box& B2)
  {
	  if (B1.Volume() != B2.Volume())
		  return true;
	  else
		  return false;
  }
  friend bool operator < (const Box& B1, const Box& B2)
  {
	  if (B1.Volume() < B2.Volume())
		  return true;
	  else
		  return false;
  }
  friend bool operator > (const Box& B1, const Box& B2)
  {
	  if (B1.Volume() > B2.Volume())
		  return true;
	  else
		  return false;
  }
};
int main(void)
{
	Box B1{ 1,2,3 };
	Box B2{ 1,2,4 };
	if (B1 == B2)
		cout << "两个盒子一样大" << endl;
	else
	{
		if (B1 < B2)
			cout << "B1盒子小" << endl;
		else
			cout << "B2盒子小" << endl;
	}
	return 0;
}

复合赋值运算重载

复合赋值运算符应为类的成员函数。
复合赋值运算符返回其左侧运算对象的引用。
+=运算符为例:

class Complex
{
private:
	double real = 0;      //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	Complex& operator += (const Complex& Z2); /*+= 运算符重载*/
};
 /*+= 运算符重载*/
Complex& Complex::operator += (const Complex& Z2)
{
	real += Z2.real;
	imag += Z2.imag;
	return *this;
}

自增\自减运算符重载

自增\自减运算符应作为成员函数重载。

前置自增(自减)运算符重载

class Complex
{
private:
	double real = 0;      //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	Complex & operator ++();        /*前置自增运算符重载*/
};
/*前置自增运算符重载*/
Complex & Complex::operator ++()
{
	real += 1;
	imag += 1;
	return *this;
}

为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。

后置自增(自减)运算符重载

为区分前置版本和后置版本,后置版本接受一个额外的(不被使用的)int 类型的形参。
当我们使用后置运算符时,编译器为这个形参提供一个值为 0 的实参。

class Complex
{
private:
	double real = 0;      //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	Complex operator ++(int);  /*后置自增运算符重载*/
};
/*后置自增运算符重载*/
Complex Complex::operator ++(int)
{
	Complex temp;   //递增前记录原值
	temp.real = real;
	temp.imag = imag;
	real += 1;
	imag += 1;
	return temp;
}

为了与内置版本保持一致,后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用。

重载输入\输出运算符

重载输入\输出运算符必须是非成员函数。

重载输出运算符

通常情况下,输出运算符的第一个形参是一个 ostream 对象的引用。
第二个形参一般来说是一个 const 对象引用。
operator <<一般要返回它的 ostream 形参的引用。

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	/*重载输出运算符*/
	friend ostream& operator << (ostream& output,const Complex& Z);
};
/*重载输出运算符*/
ostream & operator << (ostream& output,const Complex& Z)
{
	output << Z.real <<" + "<< Z.imag << 'i';
	return output;
}

重载输入运算符

通常情况下,输入运算符的第一个形参是一个 ostream 对象的引用。
第二个形参是将要读入到的非 const 对象的引用。
operator >>通常会返回它的 istream 的引用。

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	/*重载输入运算符*/
	friend istream& operator >> (istream& input,const Complex& Z);
};
/*重载输入运算符*/
istream & operator >> (istream& input,const Complex& Z)
{
	input >> Z.real >> Z.imag;
	return input;
}

下标运算符重载

下标运算符必须是成员函数。
为了与下标的原始定义兼容,下标运算符通常以所访问元素的引用作为返回值。

class MyString
{
    private:
        char*  str = nullptr;
        unsigned int MaxSize = 0;
    public:
        MyString();             /*默认构造函数*/    
        MyString(const char* S);/*构造函数,写入字符串*/
        /*下标运算符重载*/
        char& operator [] (unsigned int i);
};
/*下标运算符重载*/
char& MyString::operator [] (unsigned int i) 
{
    return str[i];
}

如果一个类包含下标运算符,则它通常会定义两个版本:一个返回普通引用,另一个是类的常量成员并且返回常量引用。

函数调用运算符重载

函数调用运算符必须是成员函数。
一个类可以定义多个不同版本的调用运算符,相互之间应该在参数数量或类型上有所区别。

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	/*函数调用运算符重载*/
	Complex & operator() (double a, double b);
};
/*函数调用运算符重载*/
Complex& Complex::operator() (double a, double b) //修改实部和虚部的值。
{
	real = a;
	imag = b;
	return *this;
}

可以象使用函数一样使用对象:

 Complex Z; //默认构造
 Z(2,3);    // 函数调用运算符重载   

类型转换运算符重载

类型转换运算符(conversion operator)是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。
类型转换函数的一般形式如下所示:

operator type() const;

其中,type 表示某种类型。
类型转换运算符可以面向任意类型(除了void之外)进行定义,只要该类型能作为函数的返回类型。
因此,我们不允许转换成数组或者函数类型,但允许转换成指针或者引用类型。
类型转换运算符既没有显式的返回类型,也没有形参,而且必须定义成类的成员函数。
类型转换运算符通常不应该改变待转换对象的内容,因此,类型转换运算符一般被定义成 const 成员。

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	/*类型转换运算符重载*/
	operator int() const; //将复数类型转换为整型数
};
/*类型转换运算符重载*/
Complex::operator int() const
{
	return real;
}

隐式类型转换

Complex Z{2,3};
int a = Z; // a = 2
unsigned int b = Z //也可以执行,a = 2

强制类型转换

Complex Z{2,3};
cout << (int)Z <<endl; //输出2
cout << (unsigned int)Z <<endl; //也能输出2

显式的类型转换运算符

C++11新标准引入了显式的类型转换运算符(explicitconversion operator)

class Complex
{
private:
	double real = 0;    //复数的实部
	double imag = 0;    //复数的虚部
public:
	Complex() = default;	 	  /*无参构造*/
	Complex(double a, double b);  /*有参构造*/
	/*类型转换运算符重载*/
	explicit operator int() const; //将复数类型转换为整型数
};
/*类型转换运算符重载*/
Complex::operator int() const
{
	return real;
}

当类型转换运算符是显式的时,必须通过显式的强制类型转换才可以执行类型转换。

Complex Z{2,3};
cout << (int)Z <<endl; //只能强制类型转换,输出2

但是当表达式出现在下列位置时,显式的类型转换将被隐式地执行:

  • if、while 及 do 语句的条件部分。
  • for 语句头的条件表达式。
  • 逻辑非运算符!、逻辑或运算符||、逻辑与运算符&&的运算对象。
  • 条件运算符?:的条件表达式。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

往昔的恒纳兰那

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值