C++中operator的兩種用法
首先宣告,轉載自:http://blog.csdn.net/skdkjzz/article/details/45872801
轉換函式的名稱是型別轉換的目標型別,因此,不必再為它指定返回值型別;轉換函式是被用於本型別的數值或變數轉換為其他的型別,也不必帶引數
在寫這篇文章之前,讓我們先回顧一下編譯器通過匹配過程確定呼叫哪一個函式的匹配順序:
(1)尋找和使用最符合函式名和引數型別(包括返回值)的函式,若找到則呼叫;
(2)否則,尋找一個函式模板,將其例項化產生一個匹配的過載函式,若找到則呼叫;
(3)否則,尋找可以通過型別轉換進行引數匹配的過載函式,若找到則呼叫它。
如果以上步驟均未找到匹配函式,則這個呼叫是錯誤的;如果這個呼叫有多於一個的匹配選譯,則呼叫匹配出現二義性,也是錯誤的。
型別轉換是將一種型別的值對映為另一種型別的值。型別轉換實際上包含有自動隱含和強制的兩種。
C語言編譯系統提供的內部資料型別的自動隱式轉換規則如下:
1.程式在執行算術運算時,低型別可以轉換為高型別。
2.在賦值表示式中,右邊表示式的值自動隱式轉換為左邊變數的型別,並賦值給它。
3.當在函式呼叫時,將實參值賦給形參,系統隱式地將實參轉換為形參的型別後,賦給形參。
4.函式有返回值時,系統將自動地將返回表示式型別轉換為函式型別後,賦值給呼叫函式。
在以上情況下,系統會進行隱式轉換的。當在程式中發現兩個資料型別不相容時,又不能自動完成隱式轉換,則將出現編譯錯誤。例如:
int* p = 100;
在這種情況下,編譯程式將報錯,為了消除錯誤,可以進行如下所示的強制型別轉換:
int* p = (int *)100;
將整型數100顯式地轉換成指標型別。
建構函式具有型別轉換功能
在實際應用中,當類定義中提供了單個引數的建構函式時,該類便提供了一種將其他資料型別的數值或變數轉換為使用者所定義資料型別的方法。因此,可以說單個引數的建構函式提供了資料轉換的功能。下面通過一個例子進一步說明單引數建構函式的型別轉換功能。
程式碼如下:
class A
{
public:
A(){ m=0; }
A(doublei) { m=i; }
void print() { cout<<M<<endl}
private:
double m;
};
void main()
{
A a(5);
a=10; //a與10是不同的資料型別
a.print();
}
程式的輸出結果為:
10
在該程式中,賦值語句a=10;中,賦值號兩邊數值10和物件a是兩上不相容的資料型別,可是它卻能順利通過編譯程式,並且輸出顯示正確結果,其主要原因是得益於單引數的建構函式。編譯系統選通過標準資料型別轉換,將整型數值10轉換成double型,然後,再通過類中定義的單引數建構函式將double型數值轉換為A類型別,最後把它賦值給a。這些轉換都是自動隱式完成的。
關於上面的程式,補充一點:
Aa = 10;
和
Aa;
a= 10;
兩者是不同的,前者對a進行初使化,編譯器會嘗試將10隱式轉換為A型別,這樣將引起a的A(doublei)建構函式直接被呼叫。
後者屬於賦值語句,編譯器將建立一個臨時物件,並將10隱式轉換為A型別。如果我們顯示呼叫
(A)10;
這也將建立一個臨時物件,引起A的建構函式被呼叫。
還有一點要注意,編譯器只會進行一次隱式轉換(C時刻庫的內建型別如intshort char等)除外,下面的語句說明了這點:
m_rst->GetFields()->GetItem(nCol)->Value= (_bstr_t)sValue;
上面Value是COM的變體型別,“Value=”將引起operator= (_bstr_t)被呼叫。如果上面省略(_bstr_t),編譯器將發生錯誤,因為沒有operator= (char*)這樣的過載,編譯器不會為我們進行兩次以上的隱式轉換。
在函式呼叫過程中,運算子過載和構造也是一個函式呼叫,如果匹配的函式如無二義性,那麼將可以產生一次隱式轉換。如果上句的Value變體類只有一個operate= (_bstr_t),那麼既使這樣寫->Value= sValue; 編譯器也會試圖將sValue隱式轉換為_bstr_t型別。
還有一種情況
程式碼如下:
classA
{
int a;
public:
A(){ };
A(int_a) { a = _a; };
Operator int() { return a; }
}
有如下呼叫:
程式碼如下:
A a(10);
A a2 = (int)(int)a; //只相當於Aa2 = (int)a; 因為第一個就近已經轉成了int,第二//個就不用再轉了
比較有意思吧,A類既有將int隱式轉換A的構造,也有int()轉換函式供強制轉換,(int)(int)a將以就近原則的方式進行。如果就近轉換失敗,編譯器將報錯。比如:
程式碼如下:
classB
{
};
A a2 = (B)a;
或
A a2 = (B)10;
編譯器報這樣的錯誤:"errorC2440: “型別轉換”:無法從“int”轉換為“B”"
可知,我們自己編寫的構造和轉換函式多麼重要。
轉換函式
轉換函式又稱型別強制轉換成員函式,它是類中的一個非靜態成員函式。它的定義格式如下:
程式碼如下:
class<型別說明符1>
{
public:
operator<型別說明符2>();
…
}
這個轉換函式定義了由<型別說明符1>到<型別說明符2>之間的對映關係。可見,轉換函式是用來將一種型別的資料轉換成為另一種型別。下面通過一個例子說明轉換函式的功能。
程式碼如下:
#include
class Rational
{
public:
Rational(intd, int n)
{
den= d;
num= n;
}
operator double();//型別轉換函式
private:
int den, num;
};
Rational::operator double()
{
return double(den)/double(num);
}
void main()
{
Rational r(5, 8);
double d = 4.7;
d += r; //這句將呼叫隱式轉換,相當於d= (double)r;
cout<<d<<ENDL;
}
程式輸出結果:
5.325
由程式可知,d是一個double型數值,r是Rational類的物件,這兩個不同型別的資料進行加法之所以能夠進行是得益於轉換函式operatordouble()。為使上述加法能夠進行,編譯系統先檢查類Rational的說明,看是否存在在下轉換函式能夠將Rational型別的運算元轉換為double型別的運算元。由於Rational類中說明了轉換函式operatordouble(),它可以在程式執行時進行上述型別轉換,因此,該程式中實現了d=r;的操作。
定義轉換函式時應注意如下幾點:
(1)轉換函式是使用者定義的成員函式,但它要是非靜態的。
(2)轉換函式的不可以有返回值。(意思是宣告中不可以有返回值)
(3)轉換函式也不帶任何引數。
(4)轉換函式函式還不能定義為友元函式。
轉換函式的名稱是型別轉換的目標型別,因此,不必再為它指定返回值型別;轉換函式是被用於本型別的數值或變數轉換為其他的型別,也不必帶引數。
類中的建構函式完成其他型別到類型別的轉換,而過載強制轉換完成類型別到其他型別的轉換。
1. operator 用於型別轉換函式:
型別轉換函式的特徵:
1) 型轉換函式定義在源類中;
2) 須由 operator 修飾,函式名稱是目標型別名或目標類名;
3) 函式沒有引數,沒有返回值,但是有return
語句,在return語句中返回目標型別資料或呼叫目標類的建構函式。
型別轉換函式主要有兩類:
1) 物件向基本資料型別轉換:
物件向不同類的物件的轉換:
例程1:
//通過型別轉換函式求半徑為5的圓的面積
//並將其儲存在float型變數中列印輸出
#include <iostream>
using namespace std;
class CArea
{
float area;
public:
CArea()
{
area=0;
}
CArea(float a) //過載含有一個引數的建構函式
{
area=a;
}
void getArea()
{
cout<<area<<endl;
}
void setArea(float a)
{
area=a;
}
operator float() //型別轉換函式
{
//將面積類物件轉換為float型資料
return area;
}
};
class CCircle
{
float R;
public:
void getR()
{
cout<<R<<endl;
}
void setR(float r)
{
R=r;
}
operator CArea() //型別轉換函式
{ //將圓類物件轉為面積類物件
float area=3.1415926*R*R;
return (CArea(area));
}
};
void main()
{
CCircle cir;
CArea are;
float a;
cir.setR(5);
cir.getR(); //列印圓的半徑
are.getArea(); //列印轉換前的面積
are=cir; //將圓類物件轉為面積類物件
are.getArea(); //列印轉換後的面積
a=are; //將面積類物件轉換為float型資料
cout<<a<<endl;
}
//將面積類物件轉換為float型資料
return area;
}
};
class CCircle
{
float R;
public:
void getR()
{
cout<<R<<endl;
}
void setR(float r)
{
R=r;
}
operator CArea() //型別轉換函式
{ //將圓類物件轉為面積類物件
float area=3.1415926*R*R;
return (CArea(area));
}
};
void main()
{
CCircle cir;
CArea are;
float a;
cir.setR(5);
cir.getR(); //列印圓的半徑
are.getArea(); //列印轉換前的面積
are=cir; //將圓類物件轉為面積類物件
are.getArea(); //列印轉換後的面積
a=are; //將面積類物件轉換為float型資料
cout<<a<<endl;
}
2. operator 用於操作符過載:
操作符過載的概念:
將現有操作符與一個成員函式相關聯,並將該操作符與其成員物件(運算元)一起使用。
注意事項:
1) 過載不能改變操作符的基本功能,以及該操作符的優先順序順序。
2) 過載不應改變操作符的本來含義。
3) 只能對已有的操作符進行過載,而不能過載新符號。
4) 操作符過載只對類可用。
5) 以下運算子不能被過載:
. 原點操作符(成員訪問符)
* 指向成員的指標
:: 作用域解析符
? : 問號條件運算子
sizeof 運算元的位元組數
操作符函式的一般格式:
return_type operator op(argument list);
return_type:返回型別(要得到什麼)
op:要過載的操作符
argument list:引數列表(運算元有哪些)
例程2:
//過載大於號操作符比較兩個人的工資
#include <iostream>
using namespace std;
class employee
{
int salary;
public:
void setSalary(int s)
{
salary=s;
}
void getSalary()
{
cout<<salary<<endl;
}
bool operator >(const employee & e)//過載大於號操作符
{
if(salary > e.salary)
return true;
else
return false;
}
};
void main()
{
employee emp1,emp2;
emp1.setSalary(1000);
emp2.setSalary(2000);
if (emp1 > emp2)
{
cout<<"emp1比emp2工資高"<<endl;
}
else
{
cout<<"emlp1沒有emp2工資高"<<endl;
}
}
宣告:operator XX(); 無返回值,XX就是某個型別;
用法:XX a = (XX)obj; 假設上邊SocketAddress非abstract類,例如:SocketAddress sa; int a = (socklen_t)sa; 也就是此時會呼叫原成員函式operator XX(); 一般返回XX型別值,可以理解成型別轉換的過載!
另外:一個自定義類的 建構函式可以用作隱形的型別轉換,
例如:Class A { A(int i) { val = i}; private int val;} , A a = 5;
解:5 首先通過建構函式A(int)隱形的轉換為A型別,然後呼叫預設的operator=賦值函式,賦值給a;