C++运算符重载的妙用
来源:互联网 发布:fliqlo mac 怎么安装 编辑:程序博客网 时间:2024/04/29 17:27
运算符重载(Operator overloading)是C++重要特性之一,本文通过列举标准库中的运算符重载实例,展示运算符重载在C++里的妙用。具体包括重载operator<<,operator>>支持cin,cout输入输出;重载operator[],实现下标运算;重载operator+=实现元素追加;重载operator()实现函数调用。如果你对C++的运算符重载掌握的游刃有余,那就无需继续往下看了。
运算符重载带来的好处就是——让代码变得简洁。下面将展示几个标准库因使用运算符重载而是代码简洁的实例。
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>定义的:
extern template ostream& operator<<(ostream&, const char*);
容器与operator[]
下面展示vector和map因提供了operator[]而使程序变得简洁的实例。
vector::operator[]
STL 容器(Container)中的vector,map,都提供了operator[],对于vector,operator[]使得它的用法“和数组类似”,就是可以用下标访问vector的元素:
int firstInt = ivec[0]; // operator[]ivec[0] = 1; //
如果没有运算符重载,同样的功能很可能就要写成:
int firstInt = ivec.get(0); ivec.set(0, 1);
这就不再像数组那么“亲切”了。
下面的代码是求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++的原始指针支持的运算有:
- 解引用(dereference)运算
- 取成员(member access)运算
- 自增(increment)、自减(decrement)运算
- 算数加减运算
实现以上功能,对应的运算符重载成员函数分别为:
- operator*()
- operator->()
- operator++()、operator--()
- 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
- C++运算符重载的妙用
- .net学习之运算符重载的妙用
- c++-运算符的重载
- C++--运算符的重载
- C指针运算的妙用
- [C/C++]运算符的重载
- 操作符重载的妙用
- C++-运算符重载
- [C++]重载运算符
- C#:运算符重载
- c++-++运算符重载
- C++:重载运算符
- 运算符重载(C++)
- [c++]运算符重载
- 【C++】运算符重载
- C++--------------------------------------------运算符重载
- C#:运算符重载
- C++:运算符重载
- 论文笔记:CNN: Single-label to Multi-label
- C++学习笔记之覆盖、重载、多态的区别
- 质因数分解(给定一个整数,求该数的所有质因数)
- 手机电池的保护电路详细介绍
- Unix/Linux操作系统:孤儿进程与僵尸进程[总结]
- C++运算符重载的妙用
- leetcode Edit Distance
- Android开发之http协议解析
- 取消chrome和safari浏览器下输入框的默认蓝框、textarea固定大小
- android menu事件
- 【Android】BinderProxy.transact / BpBinder::transact 默认同步, 异步需加FLAG_ONEWAY
- for循环的嵌套
- Mac日常操作
- vector