亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

C++ FAQ Lite[13]--算符重載(新)

2019-09-10 09:07:08
字體:
來源:轉載
供稿:網友

[13] 算符重載
(Part of C++ FAQ Lite, Copyright © 1991-2001, Marshall Cline, cline@parashift.com)

簡體中文版翻譯:申?F,nicrosoft@sunistudio.com(東日制作室,東日文檔)


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

[ Top | Bottom | Previous section | Next section ]

 

[13.2] 算符重載的好處是什么?

 

通過重載類上的標準算符,你可以發掘類的用戶的直覺。使得用戶程序所用的語言是面向問題的,而不是面向機器的。 最終目標是降低學習曲線并減少錯誤率。 [ Top | Bottom | Previous section | Next section ]

 

[13.3] 有什么算符重載的實例?

 

[Recently changed so it uses the std:: syntax (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 這里有一些算符重載的實例:
  • myString + yourString 可以連接兩個 std::string 對象
  • myDate++ 可以增加一個 Date 對象
  • a * b 可以將兩個 Number 對象相乘
  • a 可以訪問 Array 對象的某個元素
  • x = *p 可以反引用一個實際“指向”一個磁盤記錄的 "smart pointer" 它實際上在磁盤上定位到 p 所指向的記錄并返回給x。  
[ Top | Bottom | Previous section | Next section ]

 

[13.4] 但是算符重載使得我的類很丑陋;難道它不是應該使我的類更清晰嗎?

 

算符重載使得類的用戶的工作更簡易,而不是為類的開發者服務的! 考慮一下如下的例子:

class Array {
public:
  int& operator[] (unsigned i);      
// 有些人不喜歡這種語法
  // ...
};

inline
int& Array::operator[] (unsigned i)  
// 有些人不喜歡這種語法
{
 
// ...
}

有些人不喜歡operator關鍵字或類體內的有些古怪的語法。但是算符重載語法不是被期望用來使得類的開發者的工作更簡易。它被期望用來使得類的用戶的工作更簡易:  

int main()
{
  Array a;
  a[3] = 4;  
// 用戶代碼應該明顯而且易懂...
}

記住:在一個面向重用的世界中,使用你的類的人有很多,而建造它的人只有一個(你自己);因此你做任何事都應該照顧多數而不是少數。 [ Top | Bottom | Previous section | Next section ]

 

[13.5] 什么算符能/不能被重載?

 

大多數都可以被重載。C的算符中只有 . ? :(以及sizeof,技術上可以看作一個算符)。C++增加了一些自己的算符,除了::.*,大多數都可以被重載。 這是一個下標算符的示例(它返回一個引用)。先沒有算符重載:

class Array {
public:
  int& elem(unsigned i)/t{ if (i > 99) error(); return data; }
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; }
private:
  int data[100];
};

int main()
{
  Array a;
  a[10] = 42;
  a[12] += a[13];
}

[ Top | Bottom | Previous section | Next section ]

 

[13.6] 我能重載 operator== 以便比較兩個 char[] 來進行字符串比較嗎?

 

[Recently changed so it uses the std:: syntax (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 不行:被重載的算符,至少一個操作數必須是用戶定義類型(大多數時候是類)。 但即使C++允許,也不要這樣做。因為在此處你應該使用類似 std::string的類而不是字符數組,因為數組是有害的。因此無論如何你都不會想那樣做的。 [ Top | Bottom | Previous section | Next section ]

 

[13.7] 我能為“冪”運算創建一個 operator** 嗎?

 

不行。 運算符的名稱、優先級、結合性以及元數都是由語言固定的。在C++中沒有operator**,因此你不能為類類型創建它。 如果還有疑問,考慮一下x ** yx * (*y)等同(換句話說,編譯器假定 y 是一個指針)。此外,算符重載只不過是函數調用的語法修飾。雖然這種特殊的語法修飾非常美妙,但它沒有增加任何本質的東西。我建議你重載pow(base,exponent)(雙精度版本在<cmath>中)。 順便提一下,operator^可以成為冪運算,只是優先級和結合性是錯誤的。 [ Top | Bottom | Previous section | Next section ]

 

[13.8] 如何為Matrix矩陣)類創建下標運算符?

 

[Recently changed so it uses new-style headers and the std:: syntax (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.]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();/t/t/t      // 析構函數
  Matrix(const Matrix& m);/t       // 拷貝構造函數
  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[j]:

int main()
{
  Matrix m(10,10);
  m(5,8) = 106.15;
  std::cout << m(5,8);
 
// ...
}

[ Top | Bottom | Previous section | Next section ]

 

[13.9] 為什么Matrix(矩陣)類的接口不應該象數組的數組?

 

本 FAQ 其實是關于:某些人建立的Matrix 類,帶有一個返回 Array 對象的引用的operator[]。而該Array 對象也帶有一個 operator[] ,它返回Matrix的一個元素(例如,一個double的引用)。因此,他們使用類似m[j] 的語法來訪問矩陣的元素,而不是象m(i,j)的語法。 數組的數組方案顯然可以工作,但相對于operator()方法來說,缺乏靈活性。尤其是,用[][]方法很難表現的時候,用operator()方法可以很簡單的完成,因此[][]方法很可能導致差勁的表現,至少某些情況細是這樣的。 例如,實現[][]方法的最簡單途徑就是使用作為密集矩陣的,以以行為主的形式保存(或以列為主,我記不清了)的物理布局。相反,operator() 方法完全隱藏了矩陣的物理布局,在這種情況下,它可能帶來更好的表現。 可以這么認為:operator()方法永遠不比[][]方法差,有時更好。
  • operator() 永遠不差,是因為用operator()方法實現以行為主的密集矩陣的物理布局非常容易。因此,當從性能觀點出發,那樣的結構正好是最佳布局時,operator()方法也和[][]方法一樣簡單(也許operator()方法更容易一點點,但我不想夸大其詞)。
  • operator() 方法有時更好,是因為當對于給定的應用,有其它比以行為主的密集矩陣更好的布局時,用 operator() 方法比[][]方法實現會容易得多。
作為一個物理布局使得實現困難的例子,最近的項目發生在以列訪問矩陣元素(也就是,算法訪問一列中的所有元素,然后是另一列等),如果物理布局是以行為主的,對矩陣的訪問可能會“cache失效”。例如,如果行的大小幾乎和處理器的cache大小相當,那么對每個元素的訪問,都會發生“cache不命中”。在這個特殊的項目中,我們通過將映射從邏輯布局(行,列)變為物理布局(列,行),性能得到了20%的提升。 當然,還有很多這類事情的例子,而稀疏矩陣在這個問題中則是又一類例子。通常,使用operator()方法實現一個稀疏矩陣或交換行/列順序更容易,operator()方法不會損失什么,而可能獲得一些東西它不會更差,卻可能更好。 使用 operator() 方法。 [ Top | Bottom | Previous section | Next section ]

 

[13.10] 該從外(接口優先)還是從內(數據優先)設計類?

 

[Recently changed so it uses new-style headers and the std:: syntax and reworded references to STL (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.] 從外部! 良好的接口提供了一個簡化的,以用戶詞匯表達的視圖。在面向對象軟件的情況下,接口通常是單個類或一組緊密結合的類的public方法的集合. 首先考慮對象的邏輯特征是什么,而不是打算如何創建它。例如,假設要創建一個Stack(棧)類,其包含一個 LinkedList:  

class Stack {
public:
 
// ...
private:
  LinkedList list_;
};

Stack是否應該有一個返回LinkedListget()方法?或者一個帶有LinkedListset()方法?或者一個帶有LinkedList的構造函數?顯然,答案是“不”,因為應該從外向里設計接口。也就是說,Stack對象的用戶并不關心 LinkedList;他們只關心 pushing 和 popping。 現在看另一個更微妙的例子。假設 LinkedList類使用Node對象的鏈表來創建,每一個Node對象有一個指向下一個Node的指針:  

class Node { /*...*/ };

class LinkedList {
public:
 
// ...
private:
  Node* first_;
};

LinkedList類是否應該有一個讓用戶訪問第一個Nodeget()方法?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 << '';
}

實現這個接口,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 指針等)值由鏈表負責(并且只有鏈表負責,也就是說鏈表不讓用戶改變(甚至看到?。┛勺兊?tt>next 指針)。 因此 get()/set() 方法只獲取和設置鏈表的元素,而不是鏈表的底層結構。由于鏈表隱藏了底層的指針等結構,因此它能夠作非常嚴格的承諾(例如,如果它是雙重鏈表,它可以保證每一個后向指針都被下一個 Node 的前向指針匹配)。 我們看了這個例子,類的一些數據的值由用戶負責(這種情況下需要有針對數據的get()/set()方法),但對于類所控制的數據則不必有get()/set()方法。 注意:這個例子的目的不是為了告訴你如何寫一個鏈表類。實際上不要自己做鏈表類,而應該使用編譯器所提供的“容器類”的一種。理論上來說,要使用標準容器類之一,如:std::list<T> 模板。 [ Top | Bottom | Previous section | Next section ]
E-mail the author
[ C++ FAQ Lite | Table of contents | Subject index | About the author | © | Download your own copy ]
Revised Apr 8, 2001

 

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品视频播放| 黄网动漫久久久| 国模叶桐国产精品一区| 亚洲成**性毛茸茸| 久久久国产视频| 日韩中文字幕在线播放| 亚洲精品自拍视频| 精品久久久久人成| 欧美黑人巨大xxx极品| 亚洲aa在线观看| 欧美视频在线免费看| 国产精品看片资源| 国产欧美最新羞羞视频在线观看| 久久色在线播放| 欧美麻豆久久久久久中文| 国产精品999| 欧美剧在线观看| 91午夜理伦私人影院| 欧美日韩国产精品一区| 亚洲自拍av在线| 社区色欧美激情 | 欧美电影在线免费观看网站| 亚洲第一精品福利| 神马久久桃色视频| 欧美大片欧美激情性色a∨久久| 欧美在线视频一二三| 日韩精品福利网站| 精品成人av一区| 亚洲精品视频免费在线观看| 大胆人体色综合| 精品福利免费观看| 国产视频精品xxxx| 久久久精品2019中文字幕神马| 亚洲综合视频1区| 91久久久久久国产精品| 久久精品99久久香蕉国产色戒| 亚洲精品日韩丝袜精品| 在线视频免费一区二区| 亚洲第一视频在线观看| 日韩av在线高清| 欧美视频13p| 日韩亚洲第一页| 高清亚洲成在人网站天堂| 亚洲成人精品av| 91精品视频观看| 国产一区二区黑人欧美xxxx| 国产69精品久久久久9999| www.日韩视频| 精品一区二区亚洲| 欧美激情在线一区| 亚洲va国产va天堂va久久| 97精品国产aⅴ7777| 日韩有码在线播放| 欧美午夜精品久久久久久浪潮| 国产精品jizz在线观看麻豆| 亚洲欧美在线免费观看| 国产美女91呻吟求| 亚洲系列中文字幕| 欧美日韩亚洲视频| 国产精品欧美日韩一区二区| 国产欧美精品va在线观看| 亚洲欧美在线播放| 高清欧美性猛交xxxx黑人猛交| 久久久亚洲福利精品午夜| 青青草99啪国产免费| 欧美精品第一页在线播放| 亚洲激情视频在线播放| 精品久久久久久电影| 7m第一福利500精品视频| 亚洲欧美中文在线视频| 欧美日韩中文字幕| 亚洲欧洲一区二区三区在线观看| 亚洲一区二区久久| 日韩国产精品亚洲а∨天堂免| 国产一区二区丝袜| 国产精品色婷婷视频| 久久久最新网址| 精品国产91久久久久久老师| 中文字幕日韩欧美在线| 精品视频偷偷看在线观看| 国产在线高清精品| 亚洲国产精品va在线观看黑人| 亚洲第一网站免费视频| 国产精品视频一| 亚洲国产精品一区二区三区| 国产97在线|亚洲| 成人写真福利网| 成人性教育视频在线观看| 色视频www在线播放国产成人| 久久久亚洲网站| 成人福利在线观看| 日韩在线观看免费全| 色诱女教师一区二区三区| 精品国产一区二区三区久久狼5月| 亲爱的老师9免费观看全集电视剧| 91精品视频免费| 在线视频欧美日韩| 亚洲aaa激情| 久久九九亚洲综合| 国产日韩欧美成人| 日韩国产精品一区| 亚洲欧美中文字幕| 亚洲肉体裸体xxxx137| 国产午夜精品一区二区三区| 欧美区在线播放| 国产丝袜一区二区三区| 久久中文字幕国产| 成人激情在线观看| 亚洲国产精品资源| www.国产一区| 国产欧美一区二区三区四区| 久久久久999| 在线观看中文字幕亚洲| 黑人巨大精品欧美一区二区三区| 欧美福利小视频| 亚洲精品按摩视频| 国产精品电影一区| 欧美极品第一页| 亚洲影视中文字幕| 亚洲福利视频免费观看| 久久久国产精品一区| 国产亚洲xxx| 亚洲第一网站男人都懂| 69视频在线免费观看| 91国产在线精品| 中文字幕在线精品| 中文一区二区视频| 久久理论片午夜琪琪电影网| 亚洲精品xxx| 中文字幕综合一区| 久久久久久999| 日韩中文视频免费在线观看| 在线看欧美日韩| 欧亚精品中文字幕| 另类美女黄大片| 国产精品成人一区二区三区吃奶| 国产精品久久二区| 亚洲精品国产电影| 成人av资源在线播放| 欧美洲成人男女午夜视频| 狠狠躁夜夜躁人人爽天天天天97| 欧美黄色免费网站| 国产成人精品999| 亚洲精品国产成人| 91亚洲精品一区| 成人精品在线视频| 97视频在线播放| 国产精品免费久久久久影院| 亚洲高清免费观看高清完整版| 性色av一区二区三区红粉影视| 在线日韩日本国产亚洲| 国产成人啪精品视频免费网| 亚洲成年网站在线观看| 国产在线播放91| 日韩欧美国产视频| 国产精品女视频| 国产一区二区三区网站| 久久人人爽人人爽人人片亚洲| 国产亚洲一区二区精品| 国产欧美va欧美va香蕉在| 538国产精品一区二区免费视频| 欧美自拍大量在线观看| 亚洲第一免费播放区| 亚洲成人在线网|