• C++运算符重载


    C++运算符重载

     

    “<<”和“>>”本来是在C++中被定义为左/右位移运算符的,由于在iostream头文件中对它们进行了重载,所以使用它们能用作标准数据类型数据的输入和输出运算符。因此,在使用它们的程序中必须包含:#include <iostream>

     

    1. 运算符重载例子:

    例1重载函数作为Complex类的成员函数

    class Complex
    {
    public:
    	Complex operator +(Complex &c2);	//声明重载运算符’+’的函数
    	……
    };
    Complex Complex::operator + (Complex &c2)	//定义该函数
    { return Complex(real+c2.real, imag+c2.image); }
    int main()
    {
    	Complex c1(3, 4), c2(5,-10), c3;
    	c3 = c1 + c2;	//调用
    	……
    }

    分析:C++编译系统将程序中的表达式c1 + c2解释为:

           c1.operator +(c2)

    即以c2为实参调用对象c1的运算符重载函数operator +(Complex &c2)。实际上,运算符重载函数有两个参数,由于重载函数是Coplex类中的成员函数,有一个参数是隐含的,运算符函数是用this指针隐式地访问类对象的成员,如this->real+c2.real,this代表c1,即实际上是c1.real+c2.real。

     

    运算符重载函数可以是类的成员函数,也可以是类的友元函数,也可以是普通函数(不推荐)。

     

    例2 重载函数作为Complex类的友元函数

    friend Complex operator +(Complex &c1, Complex &c2);	//声明
    
    Complex operator +(Complex &c1, Complex &c2)	//定义
    { return Complex(c1.real+c2.real, c1.imag+c2.image); }
    c3 = c1+c2;	//调用

    为什么把运算符函数作为友元函数呢?理由很简单,因为运算符函数要访问Complex类对象中的成员。如果作为成员函数,就必须要求运算表达式第一个参数(即运算符左侧的操作数)是一个类对象,而且与运算符函数的类型相同,在例1中,表达式c1+c2中的第一个参数c1是Complex类对象,运算符函数返回值的类型也是Complex,这是正确的。如果c1不是Complex类,就无法启用c1.operator +(c2),也就无法通过隐式this指针访问Complex类的成员了。

    说明:有的C++编译系统(如VC++6.0)没有完全实现C++标准,它所提供的后缀.h的头文件不支持把成员函数重载为友元函数,因此在VC++6.0,应把程序的头两行:

    #include <iosteam>

    using namespace std;

    改为一行:#include <iosteam.h>   //C语言的风格

     

    例3 重载双目运算符

    bool operator >(String &string1, String &string2)
    {
    	if( strcmp(string1.p, string2.p) > 0 )	return true;
    	else		return false;
    }


    例4 重载单目运算符

    Time Time::operator ++()	//定义前置自增运算符“++”重载函数
    {
    	if( ++sec >= 60)	//隐含了this指针
    	{ sec -=60;
    	  ++minute; }
    	return *this;	//返回当前新的this指针内容
    }
    Time Time::operator ++(int) 	//定义后置“++”重载函数
    {
    	Time temp(*this);	//定义新对象temp,将当前this指针指向的对象拷贝给temp
    	sec++;
    	if( sec >= 60)
    	{ sec -=60;
    	  ++minute; }
    	return temp;	//返回未更新值的对象temp,实际上实参的值已经自增了。
    }

    说明:重载后置自增运算符时,多了一个int型的参数,增加这个参数只是为了与前置自增运算符重载函数有所区别,此外没有任何作用。

     

    例5 重载流插入/提取运算符

     

    对“<<”和“>>”重载的函数形式如下:

    istream & operator >>(istream &, 自定义类 &);       //input

    ostream & operator >>(ostream &, 自定义类 &);     //output

    例如:

    {
    	output<<”(”<<c.real<<”+”<<c.imag<<”i)”<<endl;
    	return output;
    }
    
    cout<<c3<<c2;    //调用重载后的"<<",等价于(operator <<(cout, c3))<<c2;

    编译系统把”cout<<c3”解释为:operator <<(cout, c3),即以cout和c3作为实参,调用operator <<(ostream &output, Complex &c)函数。因此,相当于执行了:

    cout<<”(”<<c3.real<<”+”<<c3.image<<”i)”<<endl; return cout;

    返回的是流提取对象cout,作为引用返回。

     

    如果有以下输出:

    cout<<c3<<c2;

    先处理cout<<c3,即(cout<<c3)<<c2;而cout<<c3其实是operator <<(cout, c3),返回的是流提取对象cout,所以cout再和后面的c2结合,输出c2的内容。可见为什么C++规定“流提取运算符重载函数的第一个参数和函数的类型都必须是ostream类型的引用”了,就是为了返回cout,以便连续输出。

     

    问:为什么要在operator前面加’&’?(即“返回引用”)

    答:开始我以为ostream & operator >>()的这个返回类型“ostream &”是为了和括号中的参数“ostream &”类型相对于,其实不然。因为只有运算符重载函数为成员函数时,才要求第一个参数和运算符重载函数的类型相同,而友元函数没有这个要求。

           先分析“ostream &”作为参数的情况,传入的第一个参数显然是cout,cout是什么,它其实也是一个类的对象,“cin和cout分别是istream类和ostream类的对象”,所以说形参定义成引用,避免了对象的拷贝。

           再来分析函数类型为什么要定义为“ostream &”,先看看原理http://blog.csdn.net/zollty/article/details/6695311。因为要使返回的对象cout能够直接使用,所以要将函数的返回值定义为引用。

    2. 不同数据类型之间的转换

    a. 标准类型数据之间的转换

    显示类型转换:

    C语言格式:(类型名)数据,例如:(int)89.5

    C++格式:类型名(数据),例如:int(89.5)

     

    b. 用构造函数进行类型转换

    几种构造函数:

    l         默认构造函数(无参,直接指定默认值):Complex();

    l         用于初始化的构造函数:Complex(double r, double i);

    l         用于复制对象的复制构造函数:Complex(Complex &c);

    l         现在要讲解的新构造函数——转换构造函数:Complex(double r){ real=r; imag=0; }(它只有一个参数)

    以上几种构造函数可以同时出现在同一个类中,它们是构造函数的重载。

     

    假如有以下声明语句:

    Complex c1(3.5);       //建立对象c1,由于只有一个参数,调用转换构造函数

    也可以用声明语句建立一个无名的Complex类对象。如:

    Complex(3.6);     //用声明语句建立一个无名对象,合法,但是无法使用

    可以在一个表达式中使用无名对象,如

    c1=Complex(3.6);      //假设c1已经被定义为Complex类对象

    若在程序中有以下表达式:

    c=c1+2.5;

    编译出错,因为不能用运算符“+”将一个Complex类对象和一个浮点数相加。可以先将2.5转换成Complex类无名对象,然后相加:

    c=c1+Complex(2.5);

    请对比Complex(2.5)和int(2.5)。可以认为Complex(2.5)的作用也是强制类型转换。通常把有一个参数的构造函数用作类型转换,所以,称为转换构造函数。如可以将一个学生类对象转换为教师类对象,可以在Teacher类中写出下面的转换构造函数:

    Teacher(Student &s)

    { num=s.num; strcpy(name, s.name); sex=s.sex; }

    但应注意:对象s中的num, name, sex必须是公用成员,否则不能被类外调用。

     

    c. 用类型转换函数进行类型转换

           用转换构造函数可以将一个指定类型的数据转换为类的对象。但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据)。C++提供了类型转换函数来解决这个问题。类型转换函数的作用是将一个类的对象转换成另一类型的数据。如果已经声明了一个Complex类:

    class Complex
    {
    public:
    	……
    private:
    	double real;
    	double imag;
    };

    可以在Complex类中这样定义类型转换函数:

    operator double()

    { return real; }

           它的作用是将一个Complex类对象转换为一个double型数据,其值是Complex类中得数据成员real的值。请注意:函数名是operator double。这点是和运算符重载时的规律一致的(都是用关键字operator开头,只是被重载的是类型名)。其特点:在函数名前面不能指定函数类型,函数也没有参数。其返回值的类型是由函数名中指定的类型名来确定的(函数返回double型变量real的值)。

           在定义了前面的数据类型转换函数后,程序中的Complex类对象是不是一律都转换成double类型的数据呢?不是的,它们具有双重身份,既是Complex类对象,又可以作为double类型数据。

           转换构造函数和类型转换运算符有一个共同的功能:当需要的时候,编译系统会自动的调用这些函数,建立一个无名的临时对象(或临时变量)。例如,若已定义d1, d2为double型变量,c1, c2为Complex类对象,且类中已经定义了类型转换函数。设程序中有以下表达式:

           d1 = d2 + c1;

    编译系统发现“+”左侧的d2是double型,而右侧的c1是Complex类对象,如果没有对运算符“+”进行重载,就会检查有无类型转换函数,结果发现有,就调用operate double函数把Complex类对象c1转换为double型数据,建立了一个临时的double变量,并与d2相加,最后将一个double的值赋给d1。相当于执行表达式:

           d1 = d2 + c1.operate double();

    如果类中已经定义了转换构造函数并重载了运算符“+”(作为Complex类的友元函数),但未对double定义类型转换函数(或者说未对double重载),若有以下表达式:

           c2 = c1 + d2;

    则系统将转换为:

           c2 = c1+ Complex(d2);

    当然,如果既有转换构造函数和运算符重载,又有double类型转换函数,出现歧义的话就会出错。

  • 相关阅读:
    Git 最全命令使用
    git 配置(实用)
    用Redis进行实时数据排名
    最长上升子序列
    KMP算法
    计算星期几【基姆拉尔森公式】
    集合划分(贝尔数)
    合数分解(质因数分解)
    乘法逆元
    扩展欧几里得算法
  • 原文地址:https://www.cnblogs.com/zollty/p/2879292.html
Copyright © 2020-2023  润新知