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

相关文章
相关标签/搜索