C++運算符重載的妙用

運算符重載(Operator overloading)是C++重要特性之一,本文經過列舉標準庫中的運算符重載實例,展現運算符重載在C++裏的妙用。具體包括重載operator<<,operator>>支持cin,cout輸入輸出;重載operator[],實現下標運算;重載operator+=實現元素追加;重載operator()實現函數調用。若是你對C++的運算符重載掌握的遊刃有餘,那就無需繼續往下看了。ios

運算符重載帶來的好處就是——讓代碼變得簡潔。下面將展現幾個標準庫因使用運算符重載而是代碼簡潔的實例。算法


Hello, World與operator<<

剛學C++時看到的第一個C++程序就是Hello World,它當時長得這樣:數組

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
	cout << "Hello, world!" << endl;
	return 0;
}

當時,我覺得 cout << sth 和 cin >> xxx 這是「必須的格式」。而事實上,這只是運算符重載在標準庫裏的一個縮影而已。這裏實際調用的是<string>定義的:dom

extern template ostream& operator<<(ostream&, const char*);


容器與operator[]

下面展現vector和map因提供了operator[]而使程序變得簡潔的實例。函數


vector::operator[]

STL 容器(Container)中的vector,map,都提供了operator[],對於vector,operator[]使得它的用法「和數組相似」,就是能夠用下標訪問vector的元素:this

int firstInt = ivec[0]; // operator[]
	ivec[0] = 1; //

若是沒有運算符重載,一樣的功能極可能就要寫成:spa

int firstInt = ivec.get(0); 
	ivec.set(0, 1);

這就再也不像數組那麼「親切」了。.net

下面的代碼是求vector<int> ivec內全部元素和的代碼:設計

int sum = 0;
	for(int i=0; i < ivec.size(); i++) {
		sum += ivec[i];
	}


map::operator[]

相似的,operator[]使map很是好用。好比使用標準庫map和string的單詞統計的核心代碼只有以下幾行:
string word;
	map<string, int> dict;
	
	while(cin >> word)
	{
		dict[word]++; // operator[]
	}

對於map,若是沒有operator[],那上面的 dict[word]++ 一行要寫成:指針

map<string, int>::iterator it = dict.find(word);
if(it != dict.end()) {
	it->second++;
}
else {
	dict.insert(make_pair(word, 1));
}

能夠從cplusplus.com能夠上看到,map的operator[]至關於:

(*((this->insert(make_pair(x,T()))).first)).second
這種寫法看起來很難理解,能這麼寫是由於map::insert是有返回值的:

pair<iterator,bool> insert ( const value_type& x );

使用C++標準庫實現的"單詞統計",整個程序以下:

#include <cstdio>
#include <iostream>
#include <map>
#include <string>

using namespace std;

int main(int argc, char *argv[])
{
	string word;
	map<string, int> dict;
	
	while(cin >> word)
	{
		dict[word]++;
	}
	
	// output:
	for(map<string, int>::iterator it = dict.begin(); it != dict.end(); ++it)
	{
		cout << it->first << "\t" << it->second << "\n";
	}
	return 0;
}
這段程序不只完成了「單詞統計」,還按照單詞的字典順序進行輸出,這些全依賴於標準庫的運算符重載。

迭代器與operator++

上面「單詞統計」的代碼,已經使用設計到了iterator,正是「迭代器」。簡單地說,迭代器就是有指針功能的class類型;而它的「指針」功能,正是經由運算符重載實現的。

好比下面代碼能夠輸出vecotr<int> ivec的所有元素:

for(vector<int>::iterator it = ivec.begin(); 
		it != ivec.end(); // operator!=
		it++) { // operator++
			printf("%d\n",
				*it); // operator*
		}

這段短短的代碼調用了iterator重載的三個operator。運算符重載使得這裏for循環的寫法和數組的迭代方式相似。C/C++的原始指針支持的運算有:

  1. 解引用(dereference)運算
  2. 取成員(member access)運算
  3. 自增(increment)、自減(decrement)運算
  4. 算數加減運算

實現以上功能,對應的運算符重載成員函數分別爲:

  1. operator*()
  2. operator->()
  3. operator++()、operator--()
  4. operator+(int)、operator-(int)

iterator至少實現了1,2,3中的一個。所有重裝就能徹底模擬指針支持的語法,要實現和指針相似的功能還需實現對於的函數內容。

除了iterator,智能指針(shared_ptr等)也重載了以上幾個運算符,使得他們用起來和原始指針很是類似(語法形式上);但它們的「自動引用計數」能力除了藉助了運算符重載,更多的應當歸功與C++的RAII慣用法,後續我將專門寫一篇關於RAII妙用的文章來解釋shared_ptr是如何實現「自動引用計數」的。

關於迭代器,最爲激進的莫過於:

copy(istream_iterator<char>(cin), istream_iterator<char>(), ostream_iterator<char>(cout, ""));


string與operator+=

標準庫的string,提供了operator[],使得用戶可使用下標運算符訪問字符串中的字符,這和char array, char pointer無異。例如:

str1[0] = str2[0];
str2[0] = 'A';

除此以外,string還提供了重載了的operator+=,能夠向已有的string對象追加字符和字符串(包括char array,char pointer)。例如:

str1 += '!';
str1 += str2;
str1 += "literal string";


函數對象與operator()

在<algorithm>提供的衆多算法中,大多都有兩個版本,其中一個版本多出一個叫作Function Object的參數,好比sort:

template <class RandomAccessIterator>
  void sort ( RandomAccessIterator first, RandomAccessIterator last );

template <class RandomAccessIterator, class Compare>
  void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp );
comp就被稱做是Function Object。

到底什麼是Function Object呢?字面理解就是一個能夠當函數調用的對象,其實就是一個重載了operator()的對象,好比要實現對一個vector<string>按照字符串長度對元素排序,能夠傳入一個這個Functor的實例:

struct StrLenComp
{
	bool operator()(const string& a, const string& b) {
		return a.length() < b.length();
	}
};
固然,若是你對C++11很熟悉,這個Functor的Function Object徹底能夠用一行的lambda表達式表示:

[](const string& a, const string& b){ return a.length() < b.length(); }


小結

上面列出的是標準庫中最廣爲認知的運算符重載的例子,但標準庫使用運算符重載的地方遠不止此。


實質

C++中運算符重載實際上和函數重載、成員函數重載並無兩樣,只是寫起來更簡潔一點罷了。編譯時,它們都會被修改成編譯器內部的名稱,也一樣支持「重載」——參數列表不一樣。


代碼實例

想看C++中運算符重載函數具體如何實現的,能夠點擊:http://blog.csdn.net/xusiwei1236/article/details/39528813

相關文章
相關標籤/搜索