#4:C++:运算符的重载;

      运算符的重载 


     所谓重载,就是重新赋予新的定义,也就是一名多用;

        
        除了函数之外,在 C++中 运算符也可以重载

例:通过函数实现两个复数的相加:

class Complex
{
	public:
		Complex() 
		{
			real = 0;
			imag = 0;
		}
		Complex(double r,double i) : real(r),imag(i) { }
		
		Complex Complex_Add(Complex &);
		
		void Display();
		
	private:
		
		double real;
		double imag;
};

Complex Complex :: Complex_Add(Complex &c2)
{
	Complex temp;
	
	temp.real = this->real + c2.real;
	temp.imag = this->imag + c2.imag;
	
	return temp;
}

void Complex :: Display()
{
	cout << "The complex is : " << real << " + " << imag << "i" << endl;
}

int main()
{
	Complex c1(3,4),c2(1.2,-4),c3;
	
	c3 = c1.Complex_Add(c2);
	
	cout << "c1 = " ; c1.Display();
	cout << "c2 = " ; c2.Display();
	
	cout << "c1 + c2 = " ; c3.Display();
	
	return 0;
}


    这种调用方式太过繁琐,所以能不能用 + 运算符来实现两个复数的相加呢?


      运算符重载的方法:

 
    运算符重载的方法是 : 定义一个重载运算符的函数;
    
    在需要执行被重载的运算符时,系统就自动调用该函数以完成运算;
    
    运算符重载实质上就是函数的重载!
    
一般格式:

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

例:对 + 进行复数加法的运算符重载:

     Complex operator + (Complex &c1, Complex &c2);
    
其中:
    1. operator 是关键字,专门用于定义重载运算符函数的;
    
    2. 运算符名称就是 C++ 提供给用户的预定义运算符;
    

注:

    3. 函数名是由 operator和运算符组成:  即 operator + 为函数名 !!!


    4. 两个形参是 Complex 类对象的引用,要求两个实参是 Complex 类型!!
    

    在定义了重载运算符之后,即: 函数 operator+ 重载了运算符 + ;
    
    此后,在执行 c1 + c2 的表达式的时候:
    

        系统就会调用 operator+ 函数,把 c1,c2 作为实参,与形参进行虚

实结合;

        

例:

    两个整数相加 :
    
        int operator+ (int a, int b)
        {
            return (a+b);
        }

    此时如果有表达式 5+8 ,就调用这个函数,将 5和8作为实参,函数的

返回值为13;

    
    
例:利用重载运算符实现两个复数相加:

class Complex
{
	public:
		Complex() 
		{
			real = 0;
			imag = 0;
		}
		
		Complex(double r,double i) : real(r),imag(i) { }
		
		Complex operator+ (Complex &);
		
		void Display();
		
	private:
		
		double real;
		double imag;		
};

Complex Complex :: operator+ (Complex &c2)
{
	Complex temp;
	
	temp.real = real + c2.real;
	temp.imag = imag + c2.imag;
	
	return temp;
}

void Complex :: Display()
{
	cout << "The complex is : " << real << " + " << imag << "i" << endl;
}

int main()
{
	Complex c1(3,4),c2(1.2,-4),c3;
	
	c3 = c1 + c2;
	
	cout << "c1 = " ; c1.Display();
	cout << "c2 = " ; c2.Display();
	
	cout << "c1 + c2 = " ; c3.Display();
	
	return 0;
}

说明:
    
     C++ 编译系统将把表达式 c1 + c2 解释为 :
    
          c1.operator+(c2)     //    c1 和 c2 都是 Complex 类的对象;
        
即:

    以 c2 为实参调用 c1 的运算重载函数 operator+(Complex &c2), 进行

求值;

    
    
重载函数的简便写法:

Complex Complex :: operator+ (Complex &c2)
{

    return Complex(real + c2.real,imag + c2.imag);

}

    其中的 Complex(xx,xx) 是建立一个临时变量,是一个无名对象,在建

立时使用构造函数;


说明:

    
    在运算符被重载之后,其原有的功能还是被保留,同时又增加了别的功能;
    

    编译系统通过对上下文( 运算符的两侧,单目运算符为一侧 ) 数据类型进

行判断。

    
    C++ 提供的运算符重载使得用户可以自己编写对 对象进行的操作;
   

      重载运算符的规则:


        1. 只能对 C++ 中已有的运算符进行重载;
        
        2. 五个不能重载的运算符:
        
            .              成员访问
            .*            成员指针访问
            ::             域运算符
            sizeof     长度运算符
            ?:            条件运算符
            
        前两个不能重载是为了保证访问成员的功能不被改变;
        
        域运算符和sizeof 运算符的运算对象是类型,而不是变量或一般表达式,不具备重载功能;
        
    
        3. 重载不能改变运算符运算对象的个数:
        
             双目运算符需要两个参数,单目运算符需要一个参数。。
            
        4. 重载不能改变运算符的优先级别;
        
        5. 重载不能改变运算符的结合性:
        
            如:赋值运算符是右向左,重载后不变;
            
        6. 重载运算符函数不能带有默认参数!
        
        7. 重载运算符必须和用户自定义类型的对象一起使用:
        
            其参数至少有一个类对象(或类对象的引用)!!!
            
    即:    不能全部是 C++ 的标准类型!!
    
例:
    int operator+ (int a, int b)
    {
        return (a-b);
    }

此时在计算时系统 对于加减将会不明确!

            如果有两个参数,则既可以都是类对象,也可以一个是类对象,一个是C++

的标准类型:

            
例:
    Complex operator+ (int a, Complex &c)
    {
        return Complex(a + c.real, c.imag);
    }

    即,一个整数和一个复数的加法运算;


        8. 用于类对象的运算符一般需要重载,但是有两个例外:
        
            运算符  = 和 &  可以不用重载,
            
            1): 赋值运算符 = :
            
                可以用于每一个类对象,可以直接用于同类对象之间相互赋值。
                
    由于系统已经为用户的每一个新声明的类重载了一个运算符,作用是逐个复制类的成员。
    
        但是有的时候,默认的对象赋值运算符不能满足程序的要求!!
        
例:数据成员中包括动态分配内存的指针成员时,在复制成员时可能会出现危险,此时要求用户自己编写。

            2):    地址运算符 & :
            
                返回类对象在内存中的起始地址;
                
                
        9. 一般应当使重载运算符的功能类似于作用于标准类型数据时的功能,
        
            防止可读性差。
            
        10. 运算符重载函数,可以是普通函数,类的成员函数或者类的友元函数;
        

                          

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


    对于 + 双目运算符,为何在例子中只有一个参数
        
        实际上运算符重载函数有两个参数,但是由于重载函数是 Complex 类中的成员函数;
    
        有一个参数是隐含的,运算符函数是通过 this 指针来访问的!!!

        
即:
    重载函数访问了两个参数 : this->real , c2.real;
    

    在将运算符函数重载为成员函数后,如果出现含该运算符的表达式,
    
        c1 + c2 编译系统将改为 :     c1.operator+ (c2)
        
    即通过对象 c1 调用运算符重载函数,并以表达式中的第二个参数( c2 ) 作为实参。
    
    
            重载函数除了作为成员函数外,还可以作为非成员函数:
            
例:作为 Complex 的友元函数:

class Complex
{
	public:
		Complex()
		{
			real = 0;
			imag = 0;
		}
		
		Complex(double r,double i) : real(r),imag(i) {}
		
		friend Complex operator + (Complex &c1, Complex &c2);
		void Display();
		
	private:
		double real;
		double imag;
		
}; 

Complex operator + (Complex &c1, Complex &c2)
{
	return Complex(c1.real + c2.real,c1.imag + c2.imag);
}

void Complex :: Display()
{
	cout << "The complex is : " << real << " + " << imag << "i" << endl;	
}

int main()
{
	Complex c1(3,4),c2(1.2,-4),c3;
	
	c3 = c1 + c2;
	
	cout << "c1 = " ; c1.Display();
	cout << "c2 = " ; c2.Display();
	
	cout << "c1 + c2 = " ; c3.Display();
	
	return 0;
}

    当运算符函数不作为成员函数的时候:
        
        c++ 把程序中的表达式 c1 + c2 解释为 : operator + (c1, c2);
        
    即执行语句:
    
        Complex operator + (Complex &c1,Complex &c2)
        {
            ....;
        }



重载函数 作为 友元函数和成员函数 的比较

     1. 作为成员函数:

        可以通过 this 指针自由的访问数据成员,因此 可以少写一个函数的参数;
        
        但 必须要求:
            
             运算表达式的第一个参数( 运算符左侧的操作数 )是一个类对象!!
        
             并且与运算符函数的类型相同;
        
            且只有返回值类型与该对象同类型,运算结果才有意义。

    

如:

    将一个复数和一个整数相加, c1 + i ,如果将其作为成员函数有:

Complex Complex :: operator + (int &i)
{
    return Complex(real + i,imag);
}

注意:

    在表达式中的重载运算符 + 左侧必须为 Complex 类型的对象
    
        c3 = c2 + i;    //    正确;
    
    如果写为了:
        
        c3 = i + c2;    //    错误,左侧不是类对象 !!

    2. 重载函数作为友元函数:
    
        运算符左侧作为 C++ 标准类型或者是非 Complex 型的对象
        
        此时运算符重载函数不能作为成员函数,只能作为非成员函数;
    
    并且如果此时函数需要访问类的私有成员,则必须声明为该类的友元函数!!
    
例: 在 Complex 中 声明重载运算符为该类的友元函数:

    friend Complex operator + ( int &i, Complex &c );
    
同时在类外定义友元函数:

    Complex operator + (int &i, Complex &c)
    {
        return Complex(i + c.real, c.imag);
    }

注:

    1. 将双目运算符作为友元函数时,函数的形参表列中必须有两个参数,不可省

略!!!

    
    2. 形参的顺序任意,不要求第一个对象为类对象!
    
    3. 但是在使用运算表达式中:
    
        要求运算符左侧的操作数与函数的第一个参数对应;
        
            运算符右侧的操作数与函数的第二个参数相对应。
            
如:

    c3 = i + c2;    //    正确,类型匹配;
    
    c3 = c2 + i;    //    错误,类型不匹配!!!
    
        即在此处数学上的交换律并不适用,需要再一次重载运算符 + :
        
Complex operator + ( Complex &c ,int &i )
{
    return Complex(i + c.real,c.imag);
}

    此后,i + c2 与 c2 + i 都合法;
    
注: 不可把两个函数都作为成员函数;

    
    3. C++ 规定,有的运算符必须定义为类的成员函数:
    
                赋值运算符,下标运算符,函数调用运算符;
    
                有的运算符不能定义为类的成员函数:            
    
                流插入 << , 流提取 >> , 类型转换运算符;
            
        所以一般将:
        
            单目运算符重载为成员函数,双目运算符重载为友元函数;
           

      重载双目运算符:

        双目运算符有两个操作数( 通常在操作符两侧 ),所以在重载函数中有两个参数:

例:

    定义一个字符串类 string,用来存放不定长的字符串;
    并且重载运算符 == ,> , <, 用于两个字符串的等于,大于,小于的比较运算。
   

class String
{
	public:
		String()
		{
			p = NULL;
		}
		String(char *str)
		{
			p = str;
		}
		
		void Display();
		void Length();
		friend bool operator >  (String &str1, String &str2);
		friend bool operator <  (String &str1, String &str2);
		friend bool operator == (String &str1, String &str2);
		
	private:
		char *p;
		int length;
		
}; 

void String :: Display()
{
	cout << p;
}

void String :: Length()
{
	for(length = 0;	*p != NULL;	p++,length++)
	{
		;
	}
	
	cout << "The length of the string is : " << length << endl;
}

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

bool operator < (String &str1, String &str2)
{
	if(strcmp(str1.p , str2.p) < 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

bool operator == (String &str1, String &str2)
{
	if(strcmp( str1.p , str2.p ) == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
	
}

void Compare(String &str1, String &str2)		//	增加一个compare 函数,减轻main函数的负担; 
{
	if( ( operator > (str1,str2) ) == 1 )       
	{
		str1.Display();
		cout << '>';
		str2.Display();
	} 
	else if( ( operator < (str1,str2) ) == 1 )       
	{
		str1.Display();
		cout << '<';
		str2.Display();
	}
	else if( ( operator == (str1,str2) ) == 1 )       
	{
		str1.Display();
		cout << '=';
		str2.Display();
	}
}

int main()
{
	String str1("BOOK!"),str2("BOOK!");
	
	Compare(str1,str2);
	
	return 0;
}


      重载单目运算符:

        单目运算符只有一个操作数,与双目运算符的重载类似:
        
        只不过重载函数只有一个参数,且如果作为成员函数,还可以省略!
        
例:
    自加 ++ 运算符的重载: 时钟的自加;

class Time
{
	public:
		Time()
		{
			minute = 0;
			sec = 0;
		}
		Time(int m,int s) : minute(m),sec(s) {}
		
		Time operator ++ ();
		
		void Display();
		
	private:
		
		int minute;
		int sec;
		
};

Time Time :: operator ++ ()
{
	(this->sec)++;
	if(sec >= 60)
	{
		sec = 0;
		minute++;
	}
	return *this;		//	返回的是一个指向正在操作的这个 Time类型的对象的指针! 
}

void Time :: Display()
{
	cout << this->minute << " : " << this->sec << endl;
}


int main()
{
	Time time1(34,0);
	
	for(int i = 0; i < 61;	i++)
	{
		++time1;			//	注: 此时必须写为 ++XXX, 即写在自加运算符后(右结合性)!! 
		time1.Display();
	}
	return 0;
} 


例: 前置自加运算符 和 后置自加运算符:

class Time
{
	public:
		Time()
		{
			minute = 0;
			sec = 0;
		}
		Time(int m,int s) : minute(m),sec(s) {}
		
		Time operator ++();
		Time operator ++(int);
		
		void Display();
		
	private:
		
		int minute;
		int sec;

}; 

Time Time :: operator ++ ()			// 前置自增运算符 ++time : 先自加再返回! 
{
	if( ++sec >= 60 )				// 此处不可写为 sec ++ !!!!; 
	{
		sec -= 60;
		minute++;
	}
	
	return *this;
}

Time Time :: operator ++ (int)		// 后置自增运算符 time++ : 返回的是自加前的对象!! 
{
	Time temp(* this);		//	把之前的 this 内容保存起来; 
	
	sec++;
	
	if(sec >= 60)
	{
		sec -= 60;
		++minute;
	}
	
	return temp;		//	返回的是自加前的对象!
}

void Time :: Display()
{
	cout << this->minute << " : " << this->sec << endl;
}

int main()
{
	Time time1(34,59),time2;
	
	cout << "time1 : ";
	time1.Display();
	
	++time1;
	cout << "time1 : ";
	time1.Display();	
	
	time2 = time1++;		//	把自加前的 time1 赋值给 time2 ,然后 time1 自加; 
	cout << "time1 : ";
	time1.Display();
	cout << "time2 : ";
	time2.Display();
		
	return 0;
} 

注:

    1. 前置自增运算符 和 后置自增运算符的区别:
    
         1) : 前置自增 ++time :
             
                先自加,返回的是自加后的值;
                
         2) : 后置自增 time++ :
        
                 先返回原值,之后再自加一!!

    2. 如果不区分前置和后置,则使用operator++( )或operator--( )即可;
        
        否则:使用operator++( )或operator--( )来重载前置运算符!!

        使用operator++(int) 或 operator--(int)来重载后置运算符,调用时,参数int被传递给值0。


      重载流插入和流提取运算符:

    C++ 中的流插入 << 和 流提取 >> 功能是在类库中提供的,即 istream 和 ostream;
    
    在类库的头文件中已经包括了输出 标准类型的数据,但是对于用户自定义的类,需要自行定义:
    
重载的形式函数:

    istream & operator >> ( istream & , 自定义类 & );
    
    ostream & operator << ( ostream & , 自定义类 & );
    
即:
    重载运算符 >> 函数的第一个函数和函数的类型都必须是 istream & 类型;
    
    第二个参数是要进行输出操作的类。
    
    所以!!! 只能把 <<  >> 的重载函数作为友元函数或者普通的函数,而不可作为成员函数!
    
1. 重载流插入运算符 << :

    用插入运算符 << 来输出用户自己的类对象的信息。

class Complex
{
	public:
		Complex()
		{
			real = 0;
			imag = 0;
		}
		Complex(double r,double i) : real(r),imag(i) {}
		
		Complex operator + (Complex &c2);
		friend ostream& operator << ( ostream & , Complex & );
		
	private:
		
		double real;
		double imag;
};

Complex Complex :: operator + (Complex &c2)			//	重载 + 运算符! 
{
	return Complex(real + c2.real, imag + c2.imag);
} 
		
ostream& operator << (ostream &output, Complex &c2)	//	重载流插入 << 运算符 
{
	output << "(" << c2.real << " + " << c2.imag << "i)" << endl;
	
	return output; 
}		
	
int main()
{
	Complex c1(2,4),c2(6,10),c3;
	
	c3 = c1 + c2;
	
	cout << c3;
	
	return 0;
}

说明:
    

    1. 运算符重载中的 形参 output 是 ostream类对象的引用名, 是用户可以任意取

名的!

    2. 编译系统把 cout << c3 解释为 :
    
            operator << ( cout , c3 ); 所以调用用户自定义的重载函数!
            
    3. return output 就是将输出流 cout 的现状返回,即保留输出流的现状:
    
例:
    cout << c2 << c3;
    
则先处理 (cout << c2) << c3;

此后相当于 cout(新值) << c3;  此时 << 左侧依旧是 ostream类型,右侧还是

Complex 类型,还可继续调用;


    所以 C++ 规定 << 重载时的第一个参数和函数的类型必须都是 ostream 类型的引

用:
        就是为了返回 cout 的当前值以便于连续输出!
        
    
2. 重载流提取运算符 >> :

    c++ 预定义的 >> 的作用是从一个输入流中提取数据;
    
        而重载的目的是为了提取用户需要的自定义类型的对象的信息;

例:       

class Complex
{
	public:
		Complex()
		{
			real = 0;
			imag = 0;
		}
		Complex(double r, double i) : real(r),imag(i){ }

		friend ostream & operator << ( ostream & , Complex & );
		friend istream & operator >> ( istream & , Complex & );		
		
	private:
		
		double real;
		double imag;

};

ostream& operator << (ostream &output, Complex &c)
{
	output << "(" << c.real << " + " << c.imag << "i)"; 
	return output;
}

istream& operator >> (istream& input, Complex &c)
{
	cout << "Input the part & the imaginary part of the complex number :";
	input >> c.real >> c.imag;
	return input;
}

int main()
{
	Complex c1,c2;
	cin >> c1 >> c2;
	cout << " c1 = " << c1 << endl;
	cout << " c2 = " << c2 << endl;
	
	return 0;
}

说明:
    

    1. 在执行 cin >> c1时,同样的调用, operator >> 函数,将 cin 和 c1 传递给形

参;

    2. 函数返回新的 cin 值,即用 cin 可以连续的从输入流提取数据给 Complex 对

象!

注:3. 每遇到一个 >> 就调用一次函数!


例改:

        当出现负数时,将出现 3 + -10i 的错误;需要改:

class Complex
{
	public:
		Complex()
		{
			real = 0;
			imag = 0;
		}
		Complex(double r, double i) : real(r),imag(i){ }

		friend ostream & operator << ( ostream & , Complex & );
		friend istream & operator >> ( istream & , Complex & );		
		
	private:
		
		double real;
		double imag;

};

ostream& operator << (ostream &output, Complex &c)
{
	output << "(" << c.real;
	if(c.imag >= 0)
	{
		output << " + ";
	}
	output << c.imag << "i)" << endl;
	return output;
}

istream& operator >> (istream& input, Complex &c)
{
	cout << "Input the part & the imaginary part of the complex number :";
	input >> c.real >> c.imag;
	return input;
}

int main()
{
	Complex c1,c2;
	cin >> c1 >> c2;
	cout << " c1 = " << c1 << endl;
	cout << " c2 = " << c2 << endl;
	
	return 0;
}

注:

    利用引用类型作为函数的形参可以在调用函数的时候:
    
    1. 不是用值传递的方式进行虚实结合,而是通过传地址的方式使形参成为别名;
    
        因此不生成临时变量,减少了时间和空间上的开销!
        

    2. 重载函数的引用对象如果是对象的引用时,返回的不是常量,而是引用所代表的

对象;

         所以可以出现在赋值号的左侧,成为左值,可以被赋值或者进行其他操作 (连续

输入,输出等)


      不同数据类型间的转换:

                            1. 标准类型数据间的转换:
            
    1. 隐式类型转换:

    
        C++ 根据运算式中的数据类型自动完成;
        
    2. 显式类型转换:
    
        数据类型名(数据) ;
        
例:    int(89.4);    即把 89.4 转换为 89;

注: 在 C语言中为: (类型名)数据 ;

    对于用户自己声明的类,需要定义专门的函数来处理数据类型转变!
    

1. 转换构造函数:

    将一个其他类型的数据转化为一个指定的类的对象。

原来学过的: 1) : 初始化构造函数;Complex(double r,double i);    
        
             2) : 复制构造函数: Complex(Complex &c)
        
说明:

    1. 转换构造函数只有一个参数:

例:

Complex(double r)        
{
    real = r;
    imag = 0;
}

   作用为 : 将 double 类型的参数 r 转化为 Complex 类的对象;(即 r为实部,虚部

为零);

    2. 在类体中,可以有转换构造函数,也可以没有转换构造函数!!
    

    3. 以上学过的几类构造函数可以同时出现在同一个类体之中,他们都是构造函数的

重载。   

        ( 但是形式不同,编译系统会自动根据建立对象时所给实参来匹配! )

例:

    构造了转换构造函数后:

    
        1. Complex c1(3.5);        //    建立了一个将 double 3.5 转化为 3.5 + 0i的虚数;
        
        2. Complex c(3.6);        //    建立了一个无名对象;

        3. Complex c1(3.6);
        
           c = c1 + 2.5;            //    若未定义一个 Complex 和 一个 double 相加的重载则错误;
           c = c1 + Complex(2.5);     //    若定义了两个复数相加,正确!
           
    4. 转换构造函数也是一种构造函数,一般都作为类型转换。
    

若不作为类型转换:

Complex(double r)
{
    cout << r;
}    
            理论上是可行的,但是不具有实际意义!
            
            
注:5. 转换构造函数只能有一个参数!!!!

                        
2. 类型转换函数:

    利用构造转换函数可以将一个指定类型的数据转化为类的对象,但是不能反过来;
    
    所以只能通过 c++ 提供的类型转换函数来解决:
    
 即、将一个类的对象转换成另一类型的函数。

例:

    在 Complex 类中声明:

operator double()
{
    return real;        //    函数返回虚数的实部;
}

注:    函数名为 operator double 。

    一般形式为:    operator 类型名() { ... }

        1. 在函数名前不能指定函数类型,函数也没有参数!!
    
        2. 返回值的类型是由函数名指定的类型名决定的;
        
        3. 类型转换函数必须作为成员函数,因为转换的本体是本类的对象;不可作为普通或友元函数;
        
说明:

    1. double 类型经过重载之后,除了原有的含义之外,还获得了新的含义( 把Complex 转化为 double型 )

    对于编译系统,不仅能识别原有的 double 类型,还会把 Complex 类对象作为 double 类型处理!!
    
    即: Complex 相当于具有了双重国籍,在不同的场合,以不同的面貌出现;
    
    
     2. 转换构造函数 和 类型转换函数有一个共同的功能:
    
         当需要的时候,编译系统可以自动调用这些函数,自动建立临时变量!  

例:

    若定义了 double型 : d1,d2 ;    Complex 型: c1,c2;
    
    则对于程序中的表达式:
            
                d1 = d2 + c1;
                
        编译系统发现 + 左端为 double ,右侧为 Complex ;
        
            1):    首先:检查有无对 + 的运算符重载;

            

            2):    若没有:检查有无类型转换函数:

    发现 double 的重载函数后,调用该函数把 Complex 转化为 double 类型(建立一

个 double temp),


    在和 c2 相加之和,把一个 double 类型的数据赋值给 d1;
    
    对于表达式( 若定义了转换构造函数和 + 的重载,但是没有定义 double 的类型转换函数 ):
                

                c2 = c1 + d2;
                
            1):首先:检查发现 有 operator+ 函数,但是是 Complex 的友元函数( 要求两个 Complex )

                在类中没有对 double 重载,所以不能把 c1 转化为 double 然后相加;
                
                此时:    调用转换构造函数:Complex(&d2) ,产生temp 的 Complex 对象;
                
                    在调用 operator+ 函数,将两个复数相加 再赋值给 c2;
                    
        即:    c2 = c1 + Complex(d2)

例:


class Complex 
{
	public:
		Complex()
		{
			real = 0;
			imag = 0;
		}
		Complex(double r,double i) : real(r),imag(i){}
		
		Complex(double r)		//	定义转换构造函数,double 转化为 Complex;	
		{
			real = r;
			imag = 0;
		} 
		
		operator double()		//	定义类型转换函数,Complex 转化为 double; 
		{	
			return real;		
		}
		
	private:
		double real;
		double imag;	
		 
};

int main()
{
	Complex c1(3,4),c2(5,-10),c3;
	double d;
	
	d = 2.5 + c1;			// double + Complex;
	
	cout << d << endl;
		
	return 0;
}

说明:

    1. 由于已经定义了成员函数 operator double,就可以利用它将Complex对象转

为double类型;

    
注:    程序中不必显示的调用类型函数,是自动被调用的(隐式调用);

    即编译系统在处理表达式 2.5 + c1 的时候,发现运算符 + 左侧为 double 类型,

右侧为 Complex 类型,   

        而又无运算符 + 重载函数,不能直接相加;
        
        但是有对 double 的重载函数,因此调用该函数,并返回double;

    2. 如果改为:
    
            d = c1 + c2;    //    d 为 double 类型;
        
        将 c1 和 c2 两个类对象相加,得到一个临时的 Complex 对象,而又有对double类型的重载函数,
        
        于是调用此函数,把临时类对象转换为 double 数据,然后复制给 d;

使用类型转换函数的好处:

    例:    程序需要对一个 double 和 Complex类对象进行运算,如果不用类型转换

函数, 就要对多种运算符进行重载,以便能进行各种运算!

            但是如果对 double 类型进行重载(使 Complex 类对象转化为 double 类型

据),就不必对各种运算符进行重载了;

            
例:包含转换构造函数、运算符重构函数和类型转换函数的程序:

1. 只包含转换构造函数和运算符重构函数:

class Complex
{
	public:
		
		Complex()			//	默认构造函数; 
		{
			real = 0;
			imag = 0;
		}
		Complex(double r)	//	转换构造函数; 
		{
			real = r;
			imag = 0;
		}
		Complex(double r, double i) : real(r),imag(i) {}	//	初始化构造函数; 
		
		friend Complex operator + (Complex c1, Complex c2);	//	重载运算符 + 的友元函数; 
		
		void Display();
		
		private:
			double real;
			double imag;
};

Complex operator + (Complex c1, Complex c2)
{
	return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

void Complex :: Display()
{
	cout << "(" << real << " + " << imag << "i)" << endl;
}

int main()
{
	Complex c1(3,4),c2(5,-10),c3;
	c3 = c1 + 2.5;
	c3.Display();
	
	return 0;
}

在 Complex 中定义了转换构造函数,并规定了怎样构成一个复数,所以在处理 c1 +

2.5时:

    编译系统解释为:    operator + ( c1, 2.5 );
    
即:
    operator + (c1, Complex(2.5)) 把 2.5 转化为临时变量;
    
注:    如果改为 2.5 + c1 程序也是可以运行的!!!!

所以在已经定义了转换构造函数的情况下,将运算符 + 重载为友元函数,在两个复数相加时,可用交换律;

注: 如果运算符 + 重载函数不作为 Complex 类的友元函数,而作为 Complex 类

成员函数:


则不满足交换律:

    对于表达式 2.5 + c1 ,
    
    c++编译系统把它解释为 : (2.5).operator + (c1) 错误!
    
    
所以:
        
        运算符函数重载为成员函数,他的第一个参数必须是本类的对象!!
        
        当第一个操作数不是类对象时,不能将运算符函数重载为成员函数 !
        
        否则交换律不适用!
        

即:    一般情况下,将:

双目运算符函数重载为友元函数。而单目运算符则重载为成员函数;



    3. 如果一定要将运算符函数重载为成员函数,而第一个操作数又不是本类对象:
    
        只能再重载一个运算符 + 函数,其第一个参数为 double 类型,且只能是友元函数。
        
    函数原型:    friend operator + (double , Complex &)
   

例:

2. 增加类型转换函数:

class Complex
{
	public:
		
		Complex()
		{
			real = 0;
			imag = 0;
		}
		Complex(double r)
		{
			real = r;
			imag = 0;
		}
		Complex(double r, double i) : real(r),imag(i) {}
		
		operator double()		// 类型转换函数; 
		{
			return real;
		}
		
		friend Complex operator + (Complex c1, Complex c2);
		
		void Display();
		
		private:
			double real;
			double imag;
};

Complex operator + (Complex c1, Complex c2)
{
	return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

void Complex :: Display()
{
	cout << "(" << real << " + " << imag << "i)" << endl;
}

int main()
{
	Complex c1(3,4),c2(5,-10),c3;
	c3 = c1 + 2.5;
	c3.Display();
	
	return 0;
}

此时程序在编译时出现错误,因为 出现了二义性:

    1. 调用构造函数把 2.5 变为 Complex 类对象,然后调用运算符 + 相加;
    
    2. 调用类型转换函数,把 c1 转换为 double 类型,然后与 2.5 相加;

如果 要使用类型在转换函数,就必须要删除运算符 + 重载函数;




  • 18
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值