<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # [13] 運算符重載 ## FAQs in section [13]: * [13.1] 運算符重載的作用是什么? * [13.2] 運算符重載的好處是什么? * [13.3] 有什么運算符重載的實例? * [13.4] 但是運算符重載使得我的類很丑陋;難道它不是應該使我的類更清晰嗎? * [13.5] 什么運算符能/不能被重載? * [13.6] 我能重載 `operator==` 以便比較兩個 `char[]` 來進行字符串比較嗎? * [13.7] 我能為“冪”運算創建一個 `operator**` 嗎? * [13.8] 如何為`Matrix`(矩陣)類創建下標運 運算符? * [13.9] 為什么`Matrix`(矩陣)類的接口不應該象數組的數組? * [13.10] 該從外(接口優先)還是從內(數據優先)設計類? ## 13.1 運算符重載的作用是什么? 它允許你為類的用戶提供一個直覺的接口。 運算符重載允許C/C++的運算符在用戶定義類型(類)上擁有一個用戶定義的意義。重載的運算符是函數調用的語法修飾: ``` ?class?Fred?{ ?public: ???//?... ?}; ?#if?0 ???//?沒有運算符重載: ???Fred?add(Fred,?Fred); ???Fred?mul(Fred,?Fred); ???Fred?f(Fred?a,?Fred?b,?Fred?c) ???{ ?????return?add(add(mul(a,b),?mul(b,c)),?mul(c,a));????//?哈哈,多可笑... ???} ?#else ???//?有運算符重載: ???Fred?operator+?(Fred,?Fred); ???Fred?operator*?(Fred,?Fred); ???Fred?f(Fred?a,?Fred?b,?Fred?c) ???{ ?????return?a*b?+?b*c?+?c*a; ???} ?#endif ``` ## 13.2 運算符重載的好處是什么? 通過重載類上的標準運算符,你可以發掘類的用戶的直覺。使得用戶程序所用的語言是面向問題的,而不是面向機器的。 最終目標是降低學習曲線并減少錯誤率。 ## 13.3 有什么運算符重載的實例? 這里有一些運算符重載的實例: * `myString?+?yourString` 可以連接兩個 `std::string` 對象 * `myDate++` 可以增加一個 `Date` 對象 * `a?*?b` 可以將兩個 `Number` 對象相乘 * `a[i]` 可以訪問 `Array` 對象的某個元素 * `x?=?*p` 可以反引用一個實際“指向”一個磁盤記錄的 "smart pointer" —— 它實際上在磁盤上定位到 `p` 所指向的記錄并返回給`x`。? ## 13.4 但是運算符重載使得我的類很丑陋;難道它不是應該使我的類更清晰嗎? 運算符重載使得類的用戶的工作更簡易,而不是為類的開發者服務的! 考慮一下如下的例子: ``` ?class?Array?{ ?public: ???int&?operator[]?(unsigned?i);??????//?有些人不喜歡這種語法 //?... ?}; ?inline ?int&?Array::operator[]?(unsigned?i)??//?有些人不喜歡這種語法 ?{ ???//?... ?} ``` 有些人不喜歡`operator`關鍵字或類體內的有些古怪的語法。但是運算符重載語法不是被期望用來使得類的開發者的工作更簡易。它被期望用來使得類的用戶的工作更簡易: ``` ?int?main() ?{ ???Array?a; ???a[3]?=?4;???//?用戶代碼應該明顯而且易懂... ?} ``` 記住:在一個面向重用的世界中,使用你的類的人有很多,而建造它的人只有一個(你自己);因此你做任何事都應該照顧多數而不是少數。 ## 13.5 什么運算符能/不能被重載? 大多數都可以被重載。C的運算符中只有 `.`和 `? :`(以及`sizeof`,技術上可以看作一個運算符)。C++增加了一些自己的運算符,除了`::`和`.*`,大多數都可以被重載。 這是一個下標 運算符的示例(它返回一個引用)。先沒有運算符重載: ``` ?class?Array?{ ?public: ???int&?elem(unsigned?i)????????{?if?(i?>?99)?error();?return?data[i];?} ?private: ???int?data[100]; ?}; ?int?main() ?{ ???Array?a; ???a.elem(10)?=?42; ???a.elem(12)?+=?a.elem(13); ?} ``` 現在用運算符重載給出同樣的邏輯: ``` ?class?Array?{ ?public: ???int&?operator[]?(unsigned?i)?{?if?(i?>?99)?error();?return?data[i];?} ?private: ???int?data[100]; ?}; ?int?main() ?{ ???Array?a; ???a[10]?=?42; ???a[12]?+=?a[13]; ?} ``` ## 13.6 我能重載 `operator==` 以便比較兩個 `char[]` 來進行字符串比較嗎? 不行:被重載的運算符,至少一個操作數必須是用戶定義類型(大多數時候是類)。 但即使C++允許,也不要這樣做。因為在此處你應該使用類似 `std::string`的類而不是字符數組,因為數組是有害的。因此無論如何你都不會想那樣做的。 ## 13.7 我能為“冪”運算創建一個 `operator**` 嗎? 不行。 運算符的名稱、優先級、結合性以及元數都是由語言固定的。在C++中沒有`operator**`,因此你不能為類類型創建它。 如果還有疑問,考慮一下`x?**?y`與`x?*?(*y)`等同(換句話說,編譯器假定 `y` 是一個指針)。此外,運算符重載只不過是函數調用的語法修飾。雖然這種特殊的語法修飾非常美妙,但它沒有增加任何本質的東西。我建議你重載`pow(base,exponent)`(雙精度版本在`<cmath>`中)。 順便提一下,`operator^`可以成為冪運算,只是優先級和結合性是錯誤的。 ## 13.8 如何為`Matrix`(矩陣)類創建下標運算符? 用 `operator()`而不是`operator[]`。 當有多個下標時,最清晰的方式是使用`operator()`而不是`operator[]`。原因是`operator[]`總是帶一個參數,而`operator()`可以帶任何數目的參數(在矩形的矩陣情況下,需要兩個參數)。 如: ``` ?class?Matrix?{ ?public: ???Matrix(unsigned?rows,?unsigned?cols); ???double&?operator()?(unsigned?row,?unsigned?col); ???double??operator()?(unsigned?row,?unsigned?col)?const; ???_//?..._ ??~Matrix();??????????????????????????????//?析構函數 ???Matrix(const?Matrix&?m);???????????????//?拷貝構造函數 ???Matrix&?operator=?(const?Matrix&?m);???//?賦值運算符 //?... ?private: ???unsigned?rows_,?cols_; ???double*?data_; ?}; ?inline ?Matrix::Matrix(unsigned?rows,?unsigned?cols) ???:?rows_?(rows), ?????cols_?(cols), ?????data_?(new?double[rows?*?cols]) ?{ ???if?(rows?==?0?||?cols?==?0) ?????throw?BadIndex("Matrix?constructor?has?0?size"); ?} ?inline ?Matrix::~Matrix() ?{ ???delete[]?data_; ?} ?inline ?double&?Matrix::operator()?(unsigned?row,?unsigned?col) ?{ ???if?(row?>=?rows_?||?col?>=?cols_) ?????throw?BadIndex("Matrix?subscript?out?of?bounds"); ???return?data_[cols_*row?+?col]; ?} ?inline ?double?Matrix::operator()?(unsigned?row,?unsigned?col)?const ?{ ???if?(row?>=?rows_?||?col?>=?cols_) ?????throw?BadIndex("const?Matrix?subscript?out?of?bounds"); ???return?data_[cols_*row?+?col]; ?} ``` 然后,你可以使用`m(i,j)`來訪問`Matrix` `m` 的元素,而不是`m[i][j]:` ``` ?int?main() ?{ ???Matrix?m(10,10); ???m(5,8)?=?106.15; ???std::cout?<<?m(5,8); ???//?... ?} ``` ## 13.9 為什么`Matrix`(矩陣)類的接口不應該象數組的數組? 本 FAQ 其實是關于:某些人建立的Matrix 類,帶有一個返回 `Array` 對象的引用的`operator]`。而該`Array` 對象也帶有一個 `operator[]` ,它返回Matrix的一個元素(例如,一個`double`的引用)。因此,他們使用類似`m[i][j]` 的語法來訪問矩陣的元素,而不是[象`m(i,j)`的語法。 數組的數組方案顯然可以工作,但相對于`operator()`方法來說,缺乏靈活性。尤其是,用`[][]`方法很難表現的時候,用`operator()`方法可以很簡單的完成,因此`[][]`方法很可能導致差勁的表現,至少某些情況細是這樣的。 例如,實現`[][]`方法的最簡單途徑就是使用作為密集矩陣的,以以行為主的形式保存(或以列為主,我記不清了)的物理布局。相反,`operator()` 方法完全隱藏了矩陣的物理布局,在這種情況下,它可能帶來更好的表現。 可以這么認為:`operator()`方法永遠不比`[][]`方法差,有時更好。 * `operator()` 永遠不差,是因為用`operator()`方法實現以行為主的密集矩陣的物理布局非常容易。因此,當從性能觀點出發,那樣的結構正好是最佳布局時,`operator()`方法也和`[][]`方法一樣簡單(也許`operator()`方法更容易一點點,但我不想夸大其詞)。 * `operator()`方法有時更好,是因為當對于給定的應用,有其它比以行為主的密集矩陣更好的布局時,用 `operator()` 方法比`[][]`方法實現會容易得多。 作為一個物理布局使得實現困難的例子,最近的項目發生在以列訪問矩陣元素(也就是,算法訪問一列中的所有元素,然后是另一列等),如果物理布局是以行為主的,對矩陣的訪問可能會“cache失效”。例如,如果行的大小幾乎和處理器的cache大小相當,那么對每個元素的訪問,都會發生“cache不命中”。在這個特殊的項目中,我們通過將映射從邏輯布局(行,列)變為物理布局(列,行),性能得到了20%的提升。 當然,還有很多這類事情的例子,而稀疏矩陣在這個問題中則是又一類例子。通常,使用`operator()`方法實現一個稀疏矩陣或交換行/列順序更容易,`operator()`方法不會損失什么,而可能獲得一些東西——它不會更差,卻可能更好。 使用 `operator()` 方法。 ## 13.10 該從外(接口優先)還是從內(數據優先)設計類? 從外部! 良好的接口提供了一個簡化的,以用戶詞匯表達的視圖。在面向對象軟件的情況下,接口通常是單個類或一組緊密結合的類的public方法的集合. 首先考慮對象的邏輯特征是什么,而不是打算如何創建它。例如,假設要創建一個`Stack`(棧)類,其包含一個 `LinkedList`: ``` ?class?Stack?{ ?public: ???_//?..._ ?private: ???LinkedList?list_; ?}; ``` Stack是否應該有一個返回`LinkedList`的`get()`方法?或者一個帶有`LinkedList`的`set()`方法?或者一個帶有`LinkedList`的構造函數?顯然,答案是“不”,因為應該從外向里設計接口。也就是說,`Stack`對象的用戶并不關心 `LinkedList`;他們只關心 pushing 和 popping。 現在看另一個更微妙的例子。假設 `LinkedList`類使用`Node`對象的鏈表來創建,每一個`Node`對象有一個指向下一個`Node`的指針: ``` ?class?Node?{?/*...*/?}; ?class?LinkedList?{ ?public: ???//?... ?private: ???Node*?first_; ?}; ``` `LinkedList`類是否應該有一個讓用戶訪問第一個`Node`的`get()`方法?`Node`對象是否應該有一個讓用戶訪問鏈中下一個 `Node`的 `get()`方法?換句話說,從外部看,`LinkedList`應該是什么樣的?`LinkedList` 是否實際上就是一個 `Node` 對象的鏈?或者這些只是實現的細節?如果只是實現的細節,`LinkedList` 將如何讓用戶在某時刻訪問 `LinkedList` 中的每一個元素? 某人的回答:`LinkedList` 不是的 `Node` 鏈。它可能的確是用? `Node` 創建的,但這不是本質。它的本質是元素的序列。因此,`LinkedList` 抽象應該提供一個“LinkedListIterator”,并且“LinkedListIterator”應該有一個`operator++` 來訪問下一個元素,并且有一對`get()`/`set()`來訪問存儲于`Node` 的值(`Node` 元素中的值只由`LinkedList`用戶負責,因此有一對`get()`/`set()`以允許用戶自由地維護該值)。 從用戶的觀點出發,我們可能希望 `LinkedList`類支持看上去類似使用指針算法訪問數組的 運算符: ``` ?void?userCode(LinkedList&?a) ?{ ???for?(LinkedListIterator?p?=?a.begin();?p?!=?a.end();?++p) ?????std::cout?<<?*p?<<?'\n'; ?} ``` 實現這個接口,`LinkedList`需要一個 `begin()`方法和 `end()`方法。它們返回一個“LinkedListIterator”對象。該“LinkedListIterator”需要一個前進的方法,`++p` ;訪問當前元素的方法,`*p`;和一個比較運算符,`p?!=?a.end()`。 如下的代碼,關鍵在于 `LinkedList` 類沒有任何讓用戶訪問 `Node` 的方法。`Node` 作為實現技術被完全地隱藏了。 `LinkedList` 類內部可能用雙重鏈表取代,甚至是一個數組,區別僅僅在于一些諸如`prepend(elem)` 和 `append(elem)`方法的性能上。 ``` ?#include?<cassert>????//?Poor?man's?exception?handling ?class?LinkedListIterator; ?class?LinkedList; ?class?Node?{ ???//?No?public?members;?this?is?a?"private?class"_ ???friend?LinkedListIterator;???//?友員類 ???friend?LinkedList; ???Node*?next_; ???int?elem_; ?}; ?class?LinkedListIterator?{ ?public: ???bool?operator==?(LinkedListIterator?i)?const; ???bool?operator!=?(LinkedListIterator?i)?const; ???void?operator++?();???//?Go?to?the?next?element ???int&?operator*??();???//?Access?the?current?element ?private: ???LinkedListIterator(Node*?p); ???Node*?p_; ???friend?LinkedList;??//?so?LinkedList?can?construct?a?LinkedListIterator ?}; ?class?LinkedList?{ ?public: ???void?append(int?elem);????//?Adds?elem?after?the?end_ ???void?prepend(int?elem);???//?Adds?elem?before?the?beginning //?... ???LinkedListIterator?begin(); ???LinkedListIterator?end(); ???//?... ?private: ???Node*?first_; ?}; ``` 這些是顯然可以內聯的方法(可能在同一個頭文件中): ``` ?inline?bool?LinkedListIterator::operator==?(LinkedListIterator?i)?const ?{ ???return?p_?==?i.p_; ?} ?inline?bool?LinkedListIterator::operator!=?(LinkedListIterator?i)?const ?{ ???return?p_?!=?i.p_; ?} ?inline?void?LinkedListIterator::operator++() ?{ ???assert(p_?!=?NULL);??//?or?if?(p_==NULL)?throw?... ???p_?=?p_->next_; ?} ?inline?int&?LinkedListIterator::operator*() ?{ ???assert(p_?!=?NULL);??//?or?if?(p_==NULL)?throw?... ???return?p_->elem_; ?} ?inline?LinkedListIterator::LinkedListIterator(Node*?p) ???:?p_(p) ?{?} ?inline?LinkedListIterator?LinkedList::begin() ?{ ???return?first_; ?} ?inline?LinkedListIterator?LinkedList::end() ?{ ???return?NULL; ?} ``` 結論:鏈表有兩種不同的數據。存儲于鏈表中的元素的值由鏈表的用戶負責(并且只有用戶負責,鏈表本身不阻止用戶將第三個元素變成第五個),而鏈表底層結構的數據(如 `next` 指針等)值由鏈表負責(并且只有鏈表負責,也就是說鏈表不讓用戶改變(甚至看到!)可變的`next` 指針)。 因此 `get()`/`set()` 方法只獲取和設置鏈表的元素,而不是鏈表的底層結構。由于鏈表隱藏了底層的指針等結構,因此它能夠作非常嚴格的承諾(例如,如果它是雙重鏈表,它可以保證每一個后向指針都被下一個 `Node` 的前向指針匹配)。 我們看了這個例子,類的一些數據的值由用戶負責(這種情況下需要有針對數據的`get()`/`set()`方法),但對于類所控制的數據則不必有`get()`/`set()`方法。 注意:這個例子的目的不是為了告訴你如何寫一個鏈表類。實際上不要自己做鏈表類,而應該使用編譯器所提供的“容器類”的一種。理論上來說,要使用標準容器類之一,如:`std::list<T>` 模板。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看