1. 程式人生 > >#4:C++:運算子的過載;

#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 相加;

如果要使用型別在轉換函式,就必須要刪除運算子 + 過載函式;