午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

如何有效地進(jìn)行運(yùn)算符重載

 fisher60 2011-11-25
如何有效地進(jìn)行運(yùn)算符重載
重載運(yùn)算符將使代碼更清晰-只在合理使用它們.
by Bill Wagner
譯者:黃森堂(vcmfc)
C++初學(xué)者(特別是從其他語(yǔ)言“叛逃”而來(lái)的)往往視運(yùn)算符重載為一大絆腳石,害怕
改變內(nèi)建運(yùn)算符的原意(參閱“What is Operator Overloading?”)。殊不知,這卻能
使得代碼清晰,比如"C=A+B"和"C.assign(A.plus(B))",您更喜歡哪一個(gè)呢?若不重載
運(yùn)算符,您只能選擇累贅的后者了。
考慮重載運(yùn)算符時(shí),您應(yīng)站在客戶的角度,為代碼的使用者想想。不妨先自問一下,我
這樣做是提高還是降低代碼的可讀性,再由此做出恰當(dāng)?shù)臎Q定。而其中的關(guān)鍵并不在于
實(shí)現(xiàn)函數(shù)功能,而是由于每種運(yùn)算符都有其約定俗成的含義,重載它們應(yīng)是在保留原有
含義的基礎(chǔ)上對(duì)功能的擴(kuò)展,而非改變。所以應(yīng)時(shí)刻站在使用者的角度來(lái)審視,比如為
擴(kuò)展的算術(shù)類型而重載算術(shù)運(yùn)算符就是很自然的,以復(fù)數(shù)為例:
Complex c1( 1.0, -4.0 );
Complex c2( 15.0, 1.0 );
Complex c3 = c1 * c2;
c2 *= c1;
那么對(duì)于非算術(shù)類(例如一個(gè)Document類),自然就不應(yīng)當(dāng)重載算術(shù)運(yùn)算符,這會(huì)讓我
們面對(duì)一群陌生的運(yùn)算符而不知所措。從這里我們可以得到一點(diǎn)重載運(yùn)算符的心得:對(duì)
于特定的類,如果一眼能看出某個(gè)運(yùn)算符在此處的含義,那么函數(shù)實(shí)現(xiàn)往往也很容易;
若連您自己對(duì)其作用都有所遲疑,那么請(qǐng)罷手,連您自己都不能很快反應(yīng)過(guò)來(lái),何況別
人呢?
I/O組: <<, >>
這是通常IO流運(yùn)算符重載的規(guī)則,你應(yīng)該這樣寫,在實(shí)際上,大部分的客戶端預(yù)期在你
的類已實(shí)現(xiàn)這些運(yùn)算符來(lái)支持io流,如何你的類支持序列化的話,你應(yīng)該實(shí)現(xiàn)這樣功能
,除了在MFC中支持CArchive之外應(yīng)該實(shí)現(xiàn)的功能。你可以在你的類中始終實(shí)現(xiàn)這些功能
,除非你明確不支持存儲(chǔ)你的對(duì)象到文件與從文件中讀取。例如:MFC的CString支持序
列化,但CDC不支持,CString聲明一個(gè)string,它將能進(jìn)行存儲(chǔ)到文件與從文件中恢復(fù),
CDC是設(shè)備,它是操作系統(tǒng)資源但不能恢復(fù)。
以下運(yùn)算符實(shí)現(xiàn)了在任何支持序列化的類中實(shí)現(xiàn)提取與插入:
ostream& operator <<
 (ostream& o, const MyClass& c);
istream& operator >>
 (istream& I, MyClass& c);
注釋:以上兩個(gè)運(yùn)算符是公共函數(shù),非成員函數(shù),第一個(gè)參數(shù)對(duì)每一個(gè)函數(shù)來(lái)說(shuō)是相同
的,都是IO流。如果兩個(gè)運(yùn)算符是成員函數(shù),它們需要內(nèi)部的iostream類,iostream類
是標(biāo)準(zhǔn)C++庫(kù)的一部分,追加新的定義,你不能在內(nèi)部實(shí)現(xiàn)流運(yùn)算符,他們必須是公共的
,在你的類中友元你寫的函數(shù),那么就可以存取所有的內(nèi)部變量。
比較與賦值:=, !=, ==
這些運(yùn)算符是實(shí)現(xiàn)在任何類中實(shí)現(xiàn)你想拷貝值的賦值運(yùn)算,如果在你的類中寫了拷貝構(gòu)
造,那么你得實(shí)現(xiàn)這些功能,再一次強(qiáng)調(diào),這些功能是新增到你的類的公共部分,在公
共部分包含了這些運(yùn)算符并顯露它們。
所有的比較運(yùn)算符都有關(guān)系到賦值運(yùn)算,你通常在你的類中提供了拷貝構(gòu)造與賦值操作
,然后,如果你支持賦值,那么你也要支持同等的比較對(duì)象的能力,公共實(shí)現(xiàn)了!=與==
操作,在實(shí)際上,如果你明確隱含了賦值操作,比較操作仍然值得做
MFC的CRect類提供了這些所有運(yùn)算符,盡管你設(shè)想有兩個(gè)矩形相等的理由.MFC的CBrush
類不提供,將賦值操作創(chuàng)建兩個(gè)brush來(lái)指向系統(tǒng)資源或創(chuàng)建新的系統(tǒng)資源?任何一個(gè)都
可以,程序員在讀客端的CBrush賦值聲明將不明白如何管理下面的系統(tǒng)資源,客戶端程
序員可能需要知道
// as global operators:
bool operator == (const MyClass& l,
 const MyClass& r)
{
 bool isEqual;
 //... test each member,
 // set isEqual.
 Return isEqual;
};
bool operator != (const MyClass& l,
 const MyClass& r)
{
 return ! (l == r);
}
// As member functions:
bool operator == (const MyClass& r)
 const
{
 bool isEqual;
 //... test each member,
 // set isEqual.
 Return isEqual;
};
bool operator != (const MyClass& r)
 const
{
 return ! (*this == r);
}
次序關(guān)系:<, >, <=, >=
標(biāo)準(zhǔn)模板庫(kù)在它所有的查找與排序算法中使用次序關(guān)系,如果你實(shí)現(xiàn)這些功能那么你隨
時(shí)都可以在你的類中使用自然的次序比較,沒有一個(gè)MFC對(duì)象包裝GDI對(duì)象來(lái)支持這些操
作,然而,CString類卻支持所有的。
STL使用小于運(yùn)算符來(lái)進(jìn)行排序與查找的比較,且STL算法只需要小于運(yùn)算符,不需要其
它。STL通過(guò)限制這種需求小于運(yùn)算符,不要在你的類中把這種限制強(qiáng)加于所有用戶上。
我更喜歡給予客戶端存取所有的次序比較,同樣,如果類已定義了次序關(guān)系,所有同等
的運(yùn)算符都得定義,你得避免在其它條件中用函數(shù)實(shí)現(xiàn)它們,你得到避免在小組的其它
地方使用同等的函數(shù)。注意:如果我在小組中實(shí)現(xiàn)了>=,我就會(huì)實(shí)現(xiàn)<、<=、>。一般認(rèn)為
只要實(shí)現(xiàn)>與<就可以,這是錯(cuò)誤的,因?yàn)楫?dāng)兩個(gè)對(duì)象相等的時(shí)候。
bool operator < (const MyClass& r)
 const
{
 // ... test each member.
};
bool operator >= (const MyClass& r)
 const
{
 return !(*this < r)
}
bool operator > (const MyClass& r)
 const
{
 // ... test each member.
};
bool operator <= (const MyClass& r)
 const
{
 return !(*this > r);
}
遞增與遞減:++, --
這些運(yùn)算符是遞增與遞減類對(duì)象,在以下三種情形你需要重載它們:類型指示器(譯者:
像STL的Iterator),順序存取與整型類型。類型的整數(shù)模型的支持要有明確的意義。
類型指示器是個(gè)指向vector類型,數(shù)據(jù)庫(kù)光標(biāo),似類的指示器,標(biāo)準(zhǔn)C++庫(kù)提供少量有關(guān)
這方面的例子,在STL中iterator支持遞增與與遞減,在一些場(chǎng)合,這些操作進(jìn)行向前與
向后移動(dòng)對(duì)象集,增加那些函數(shù)在類來(lái)包裝數(shù)據(jù)的存??;通過(guò)遞增與遞減來(lái)向前與向后
移動(dòng)數(shù)的行數(shù)。
我通過(guò)使用順序存取來(lái)循環(huán)取得color,通過(guò)遞增與遞減來(lái)移動(dòng)到向后與向前的color上(
譯者:我覺得作者的意思:如果你要提供像操縱數(shù)據(jù)庫(kù)中的數(shù)據(jù)表的上一筆與下一筆等
功能的話,你就需要重載它)。
只要你想重載遞增與遞減運(yùn)算符,你要記住每個(gè)操作符有前遞增與后遞增的版本,以下
是遞增的例子:
int i=5;
int j = i++; // j == 5, i == 6.
j = ++i; // i == 7, j == 7.
注釋:返回值有對(duì)于前遞增與后遞增是不同的,C++語(yǔ)言定義了前遞增版本是無(wú)參數(shù),后
遞增是有一個(gè)整數(shù)參數(shù),你的類應(yīng)該象如下定義:
//前遞增,例:++i
MyClass& operator++ ()
{
 // ... whatever is necessary.
 return *this;
}
//后遞增,例:i++
MyClass operator ++ (int)
{
 // Make a copy.
 MyClass rval (*this);
 // ... whatever is necessary.
 // to increment *this.
 // Return the old value.
 return rval;
}
在通常駐,你應(yīng)該支持前遞增與后遞增的兩個(gè)版本,但有時(shí)候,只需要前遞增版本,后
遞增版本需要你拷類對(duì)象;有時(shí),拷貝對(duì)象是昂貴的代價(jià),所以只支持前遞增;當(dāng)對(duì)象使
用大多的內(nèi)存來(lái)進(jìn)行拷貝構(gòu)造的時(shí)候,我不贊成支持后遞增,類的使用者不知道沒有拷
貝構(gòu)造的支持是無(wú)法支持后遞補(bǔ)增的。
加法,減法:+, -, +=, -=
這些運(yùn)算符執(zhí)行通常駐的算術(shù)運(yùn)算,有兩種情形你需要重載它們:首先,你的類需要模
擬數(shù)學(xué)模型,第二是當(dāng)你的類是要模擬指針.通常的錯(cuò)誤是認(rèn)為只需要重載少量的運(yùn)算符
,而我認(rèn)為應(yīng)該成組重載,因?yàn)橛脩粼谑褂媚愕念悤r(shí)認(rèn)為它們已經(jīng)全部重載了。
+=與-=比二元運(yùn)算符+能更好工作,二元運(yùn)算符版本在返回結(jié)果創(chuàng)建了你的類的拷貝,當(dāng)
函數(shù)執(zhí)行時(shí)候它們創(chuàng)建了臨時(shí)對(duì)象。
MyClass operator + (const MyClass& r)
 const
{
 MyClass rVal;
 // perform operations, storing the
 // result in rVal.
 return rVal;
}
MyClass& operator += (const
 MyClass& r)
{
 // perform operations, storing the
 // result in this.
 return *this;
}
乘法: *, /, *=, /= 整除:%, %=
這些運(yùn)算符執(zhí)行通常的算術(shù)乘法與除法,像加法與減法運(yùn)算符,只有在你的類中需要模
擬處理這些類型時(shí)才需要重載它們,我分開整數(shù)乘法與除法是因?yàn)樗鼈冎挥性谡麛?shù)類型
才有意義。
相似的其它運(yùn)算符,如果你的類重載過(guò)一個(gè),那么你應(yīng)該重載相關(guān)的全部運(yùn)算符,這些
函數(shù)的原型與加法與減法是一樣的,例如:*、/相似于+,它們都有更多效的一元運(yùn)算符
(如:*=,/=),因此,當(dāng)你重載其中的一個(gè),那么人就得重載全部。
內(nèi)存管理組:new delete
C++ new與delete運(yùn)算符是在用戶需要在公共內(nèi)存中產(chǎn)生新對(duì)象或刪除已存在對(duì)象而被調(diào)
用,你可以寫類的專有版本函數(shù)或公共版本;只有在你的類對(duì)象或派生類的創(chuàng)建中使用類
的專有版本。
只有一個(gè)理由來(lái)重載這些函數(shù):重載它們來(lái)改善你的應(yīng)用程序的性能,如果你重載這些
函數(shù),你必須提供相同的界面,相同的外部行為與相同的功能。前面所說(shuō)的,很少重載
new和delete運(yùn)算符的全局的form。這么做會(huì)產(chǎn)生很嚴(yán)重的后果,因?yàn)樗鼓愕膸?kù)與跟已
經(jīng)與new和delete運(yùn)算符的標(biāo)準(zhǔn)form相連接的庫(kù)產(chǎn)生沖突。而且,它使你的代碼與所有可
能產(chǎn)生同樣結(jié)論的庫(kù)相沖突。例如,MFC使用全局的new和delete運(yùn)算符來(lái)避免內(nèi)存的泄
漏。
我創(chuàng)建了類的專有版本的new和delete,在我運(yùn)行了我的一個(gè)應(yīng)用程序的配置文件并發(fā)現(xiàn)
內(nèi)存的分配和釋放是瓶頸的時(shí)候。一般來(lái)說(shuō),在程序運(yùn)行過(guò)程中有很多小的對(duì)象被創(chuàng)建
和刪除,例如一個(gè)簡(jiǎn)單的自由手繪工具。鼠標(biāo)的每次移動(dòng)都在圖形的一系列點(diǎn)中增加一
個(gè)新點(diǎn),而每個(gè)點(diǎn)都包含兩個(gè)整數(shù)。無(wú)論何時(shí)使用者創(chuàng)建了一個(gè)新的圖形,可能會(huì)增加
成百上千的點(diǎn)。如果使用者刪除了一個(gè)圖形,可能有成百上千的點(diǎn)被刪除。
你可以通過(guò)很多的途徑調(diào)用new和delete。無(wú)論何時(shí)你重載new和delete,你要保證在所
有的form中重載是有意義的。首先,在標(biāo)準(zhǔn)的form里重載new和delete,無(wú)論何時(shí)生成一
個(gè)對(duì)象都得調(diào)用類的專有版本。第二,重載new的數(shù)組form版:運(yùn)算符new[]。如果你重
載了這兩個(gè)form,只有當(dāng)用戶需要一個(gè)對(duì)象而不是對(duì)象的數(shù)組的時(shí)候,你才可以得到你
改良版的new和delete。你需要為你寫的每個(gè)new的form寫一個(gè)delete。
要認(rèn)識(shí)到new和delete是繼承的,即使它們是靜態(tài)的函數(shù)。這意味著你的專有版本的new
和delete是被你自己的類派生出來(lái)的類所調(diào)用。偶然的是,你的類的專有版本的new和d
elete函數(shù)是基于對(duì)象的大小的,并且是你的專有的函數(shù)
class MyClass
{
public:
 // Two forms of operator new:
 static void* operator new(size_t
  size); // Regular new
 static void* operator new
  [](size_t size); // array new
 // Similar forms of operator
 // delete:
 static void operator delete (void*
  rawMemory, size_t size);
 static void operator
  delete[](void*
  rawMemory, size_t size);
}
在重載它們之前你需要充分理解所有標(biāo)準(zhǔn)版本newdelete的內(nèi)部行為,記得你重載了new
或delete,你必須重載兩者;同樣,new與delete在許多form是不同的,你要確信你能解
決所有的問題。
指針引用標(biāo)識(shí)符:->, *
使用這些運(yùn)算符來(lái)操縱指向內(nèi)存中自已的引用,如果p是指向整數(shù),那么*p的值就是整數(shù)
;如果pRect是指向CRect對(duì)象,pRect->left是矩形的左值。
只有當(dāng)你要模擬指針的時(shí)候才需要重載這些運(yùn)算符,標(biāo)準(zhǔn)庫(kù)auto_ptr定義了它們與STL中
所有的iterator,當(dāng)你需要自已的版本函數(shù),你得遵守相同的應(yīng)用規(guī)則,我建議寫兩個(gè)版
本:const對(duì)象版與隨機(jī)存取對(duì)象版,如果你不寫這兩個(gè)版本,在你的新類中適當(dāng)使用c
onst方式,你需要非const版本。因此,當(dāng)客戶端程序員使用的智能指針去修改指向?qū)ο?BR>,你需要cons版本來(lái)處理你的類的const對(duì)象,如果你沒有定義這個(gè)運(yùn)算符,你就不能使
用const智能指針去存取指向的對(duì)象。
class MyClass
{
public:
 _Ty* operator -> ();
 const _Ty* operator->() const;
 _Ty& operator* ();
 const _Ty& operator*()const;
};
函數(shù)調(diào)用標(biāo)識(shí)符:()
使用函數(shù)運(yùn)算符來(lái)處理你的類的函數(shù)類型,當(dāng)類支持函數(shù)運(yùn)算符來(lái)調(diào)用你的函數(shù)對(duì)象,在
兩種情形下你需要重載函數(shù)運(yùn)算符:以前,程序員使用這種調(diào)用來(lái)提供兩維或更多給的
數(shù)組類:
class TwoDArray {
public:
 float operator () (int x, int y)
  const;
 float& operator () (int x, int y);
};
但現(xiàn)在,在更多的場(chǎng)合里在你使用這個(gè)函數(shù)來(lái)提供函數(shù)對(duì)象來(lái)使用STL算法(在這個(gè)文檔
里它不能完全代替函數(shù)對(duì)象,可參考其它信息),例如:排序函數(shù)使用函數(shù)對(duì)象來(lái)比較對(duì)
象類型:
class CompareObjects {
public:
 // Function call operator:
 bool operator () (
  const MyClass& l,
  const MyClass& r) const
 {
  bool rVal = false;
  // various tests.
  return rVal;
 };
};
// usage:
extern vector < MyClass > array;
sort (array.begin (), array.end (),
 CompareObjects ());
注解:在兩個(gè)示例里,你可以自由定義數(shù)字與其它參數(shù)類型的函數(shù),你可以在任何地方
調(diào)用運(yùn)算符。
數(shù)組存取標(biāo)識(shí)符:[]
使用[]來(lái)存取數(shù)組中的單個(gè)元素,你需要重載它們的唯一情形:模擬數(shù)組;當(dāng)你重載這個(gè)
運(yùn)算符你需要明白兩點(diǎn):首先,[]是二元運(yùn)算符,這意味著這個(gè)運(yùn)算符只有一個(gè)參數(shù),
你不能使用這個(gè)運(yùn)算符來(lái)使用多維數(shù)組;第二,你想在數(shù)組中使用任意類型的數(shù)據(jù),這
允許你結(jié)合數(shù)組來(lái)創(chuàng)建任何類型,例如:
class MyDictionary {
public:
 // Array operator:
 string operator [] (const string&
  word) const
 {
  string definition =
   "who knows";
  // find definiton.
  return rVal;
 };
 // To change a definition:
 string& operator [] (const string&
  word);
};
// usage:
extern MyDictionary Websters;
string def = Websters["alligator"];
位操作:^, &, |, ~, ^=, &=, |=
它們都是整數(shù)值的位運(yùn)算符,我從未到達(dá)使用重載來(lái)應(yīng)用位運(yùn)算,它們只定義了整數(shù)類
型,但我找不到更好的理由來(lái)模擬整數(shù)類型。
不能使用:!, <<=, >>=, &&, ||
!是邏輯非運(yùn)算符,它不能在表達(dá)式的右邊(譯者:可參閱Guru of the week#26 Boolean
),<<=與>>=是賦值移位運(yùn)算符,它們執(zhí)行了在對(duì)象中進(jìn)行左移或右移并返回結(jié)果,使用
公共運(yùn)算符來(lái)混合使用;&&是邏輯AND運(yùn)算符,與||是邏輯OR運(yùn)算符。
我看到!運(yùn)算符來(lái)進(jìn)行任何對(duì)象的有效性測(cè)試,這在你不記得它的時(shí)候,這是個(gè)好主意,
如果對(duì)象是true,那么對(duì)象是有效的,至少到目前為止我只看到過(guò)這種用法;>>=與<<=不
能使用是因?yàn)樗鼈冎荒苓M(jìn)行位運(yùn)算,除非你的類是整數(shù)值,否則重載移位是沒有意義的
。
&&與||不能重載是因?yàn)樗鼈兣c正常的表達(dá)式計(jì)算規(guī)則沖突,逗號(hào)是不能重載的是因?yàn)槟?BR>是沒有意義的
運(yùn)算符重載是有用的技術(shù),但你需要恰當(dāng)?shù)厥褂脩?yīng)用它們,你也需要確定在客戶端什么
時(shí)候使用你重載的運(yùn)算符,使得代碼更簡(jiǎn)練與明了,始終要求你自已實(shí)現(xiàn)重載運(yùn)算符比
用正常的函數(shù)更加清楚描繪你的意圖。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多