C++ 中的运算符重载
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
可重载运算符/不可重载运算符
可重载
双目算术运算符+ (加),-(减),*(乘),/(除),% (取模)关系运算符==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于)逻辑运算符||(逻辑或),&&(逻辑与),!(逻辑非)单目运算符+ (正),-(负),*(指针),&(取地址)自增自减运算符++(自增),--(自减)位运算符| (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移)赋值运算符=, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>=空间申请与释放new, delete, new[ ] , delete[]其他运算符()(函数调用),->(成员访问),,(逗号),[](下标)
不可重载
.:成员访问运算符
.*, ->*:成员指针访问运算符
:::域运算符
sizeof:长度运算符
?::条件运算符
#: 预处理符号
一元减运算符,即负号( - )
逻辑非运算符( ! )
C++ 二元运算符重载
#include <iostream>
using namespace std;
class Box
{
double length; // 长度
double breadth; // 宽度
double height; // 高度
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Volume of Box1 : 210
Volume of Box2 : 1560
Volume of Box3 : 5400
C++ 语言支持各种关系运算符( < 、 > 、 <= 、 >= 、 == 等等),它们可用于比较 C++ 内置的数据类型。
您可以重载任何一个关系运算符,重载后的关系运算符可用于比较类的对象。
下面的实例演示了如何重载 < 运算符,类似地,您也可以尝试重载其他的关系运算符。
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
// 显示距离的方法
void displayDistance()
{
cout << "F: " << feet << " I:" << inches <<endl;
}
// 重载负运算符( - )
Distance operator- ()
{
feet = -feet;
inches = -inches;
return Distance(feet, inches);
}
// 重载小于运算符( < )
bool operator <(const Distance& d)
{
if(feet < d.feet)
{
return true;
}
if(feet == d.feet && inches < d.inches)
{
return true;
}
return false;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11);
if( D1 < D2 )
{
cout << "D1 is less than D2 " << endl;
}
else
{
cout << "D2 is less than D1 " << endl;
}
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
D2 is less than D1
C++ 能够使用流提取运算符 >> 和流插入运算符 << 来输入和输出内置的数据类型。您可以重载流提取运算符和流插入运算符来操作对象等用户自定义的数据类型。
在这里,有一点很重要,我们需要把运算符重载函数声明为类的友元函数,这样我们就能不用创建对象而直接调用函数。
下面的实例演示了如何重载提取运算符 >> 和插入运算符 <<。
实例
#include <iostream>
using namespace std;
class Distance
{
private:
int feet; // 0 到无穷
int inches; // 0 到 12
public:
// 所需的构造函数
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
friend ostream &operator<<( ostream &output,
const Distance &D )
{
output << "F : " << D.feet << " I : " << D.inches;
return output;
}
friend istream &operator>>( istream &input, Distance &D )
{
input >> D.feet >> D.inches;
return input;
}
};
int main()
{
Distance D1(11, 10), D2(5, 11), D3;
cout << "Enter the value of object : " << endl;
cin >> D3;
cout << "First Distance : " << D1 << endl;
cout << "Second Distance :" << D2 << endl;
cout << "Third Distance :" << D3 << endl;
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
$./a.out
Enter the value of object :
70
10
First Distance : F : 11 I : 10
Second Distance :F : 5 I : 11
Third Distance :F : 70 I : 10
递增运算符( ++ )和递减运算符( -- )是 C++ 语言中两个重要的一元运算符。
下面的实例演示了如何重载递增运算符( ++ ),包括前缀和后缀两种用法。类似地,您也可以尝试重载递减运算符( -- )。
实例
#include <iostream>
using namespace std;
class Time
{
private:
int hours; // 0 到 23
int minutes; // 0 到 59
public:
// 所需的构造函数
Time(){
hours = 0;
minutes = 0;
}
Time(int h, int m){
hours = h;
minutes = m;
}
// 显示时间的方法
void displayTime()
{
cout << "H: " << hours << " M:" << minutes <<endl;
}
// 重载前缀递增运算符( ++ )
Time operator++ ()
{
++minutes; // 对象加 1
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
return Time(hours, minutes);
}
// 重载后缀递增运算符( ++ )
Time operator++( int )
{
// 保存原始值
Time T(hours, minutes);
// 对象加 1
++minutes;
if(minutes >= 60)
{
++hours;
minutes -= 60;
}
// 返回旧的原始值
return T;
}
};
int main()
{
Time T1(11, 59), T2(10,40);
++T1; // T1 加 1
T1.displayTime(); // 显示 T1
++T1; // T1 再加 1
T1.displayTime(); // 显示 T1
T2++; // T2 加 1
T2.displayTime(); // 显示 T2
T2++; // T2 再加 1
T2.displayTime(); // 显示 T2
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
H: 12 M:0
H: 12 M:1
H: 10 M:41
H: 10 M:42
函数调用运算符 () 可以被重载用于类的对象。当重载 () 时,您不是创造了一种新的调用函数的方式,相反地,这是创建一个可以传递任意数目参数的运算符函数。
下面的实例演示了如何重载函数调用运算符 ()。
#include <iostream>using namespace std; class Distance{ private: int feet; // 0 到无穷 int inches; // 0 到 12 public: // 所需的构造函数 Distance(){ feet = 0; inches = 0; } Distance(int f, int i){ feet = f; inches = i; } // 重载函数调用运算符 Distance operator()(int a, int b, int c) { Distance D; // 进行随机计算 D.feet = a + c + 10; D.inches = b + c + 100 ; return D; } // 显示距离的方法 void displayDistance() { cout << "F: " << feet << " I:" << inches << endl; } };int main(){ Distance D1(11, 10), D2; cout << "First Distance : "; D1.displayDistance(); D2 = D1(10, 10, 10); // invoke operator() cout << "Second Distance :"; D2.displayDistance(); return 0;}当上面的代码被编译和执行时,它会产生下列结果:First Distance : F: 11 I:10Second Distance :F: 30 I:120
1.除了类属关系运算符”.“、成员指针运算符”.*“、作用域运算符”::“、sizeof运算符和三目运算符”?:“以外,C++中的所有运算符都可以重载。
2.重载运算符限制在C++语言中已有的运算符范围内的允许重载的运算符之中,不能创建新的运算符。
3.运算符重载实质上是函数重载,因此编译程序对运算符重载的选择,遵循函数重载的选择原则。
4.运算符重载不能改变该运算符用于内部类型对象的含义。它只能和用户自定义类型的对象一起使用,或者用于用户自定义类型的对象和内部类型的对象混合使用时。
5.运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符。
vector
vector<T> c | 产生空的vector |
vector<T> c1(c2) | 产生同类型的c1,并将复制c2的所有元素 |
vector<T> c(n) | 利用类型T的默认构造函数和拷贝构造函数生成一个大小为n的vector |
vector<T> c(n,e)
| 产生一个大小为n的vector,每个元素都是e |
vector<T> c(beg,end) | 产生一个vector,以区间[beg,end]为元素初值 |
非变动操作
操作 | 效果 |
c.size() | 返回元素个数 |
c.empty() | 判断容器是否为空 |
c.max_size() | 返回元素最大可能数量(固定值) |
c.capacity() | 返回重新分配空间前可容纳的最大元素数量 |
c.reserve(n) | 扩大容量为n |
c1==c2 | 判断c1是否等于c2 |
c1!=c2 | 判断c1是否不等于c2 |
c1<c2 | 判断c1是否小于c2 |
c1>c2 | 判断c1是否大于c2 |
c1<=c2 | 判断c1是否大于等于c2 |
c1>=c2 | 判断c1是否小于等于c2 |
赋值操作
操作 | 效果 |
c1 = c2 | 将c2的全部元素赋值给c1 |
c.assign(n,e) | 将元素e的n个拷贝赋值给c |
c.assign(beg,end) | 将区间[beg,end]的元素赋值给c |
c1.swap(c2) | 将c1和c2元素互换 |
swap(c1,c2) | 同上,全局函数 |
元素存取
操作 | 效果 |
at(idx) | 返回索引idx所标识的元素的引用,进行越界检查 |
operator [](idx)
| 返回索引idx所标识的元素的引用,不进行越界检查 |
front() | 返回第一个元素的引用,不检查元素是否存在 |
back()
| 返回最后一个元素的引用,不检查元素是否存在
|
迭代器相关函数
操作 | 效果 |
begin() | 返回一个迭代器,指向第一个元素 |
end() | 返回一个迭代器,指向最后一个元素之后 |
rbegin() | 返回一个逆向迭代器,指向逆向遍历的第一个元素 |
rend() | 返回一个逆向迭代器,指向逆向遍历的最后一个元素 |
安插元素
操作 | 效果 |
c.insert(pos,e) | 在pos位置插入元素e的副本,并返回新元素位置 |
c.insert(pos,n,e) | 在pos位置插入n个元素e的副本 |
c.insert(pos,beg,end) | 在pos位置插入区间[beg,end]内所有元素的副本 |
c.push_back(e) | 在尾部添加一个元素e的副本 |
移除元素
操作 | 效果 |
c.pop_back() | 移除最后一个元素但不返回最后一个元素 |
c.erase(pos) | 删除pos位置的元素,返回下一个元素的位置 |
c.erase(beg,end) | 删除区间[beg,end]内所有元素,返回下一个元素的位置 |
c.clear() | 移除所有元素,清空容器 |
c.resize(num) | 将元素数量改为num(增加的元素用defalut构造函数产生,多余的元素被删除) |
c.resize(num,e)
| 将元素数量改为num(增加的元素是e的副本) |
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 创建一个向量存储 int
vector<int> vec;
int i;
// 显示 vec 的原始大小
cout << "vector size = " << vec.size() << endl;
// 推入 5 个值到向量中
for(i = 0; i < 5; i++){
vec.push_back(i);
}
// 显示 vec 扩展后的大小
cout << "extended vector size = " << vec.size() << endl;
// 访问向量中的 5 个值
for(i = 0; i < 5; i++){
cout << "value of vec [" << i << "] = " << vec[i] << endl;
}
// 使用迭代器 iterator 访问值
vector<int>::iterator v = vec.begin();
while( v != vec.end()) {
cout << "value of v = " << *v << endl;
v++;
}
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
vector size = 0
extended vector size = 5
value of vec [0] = 0
value of vec [1] = 1
value of vec [2] = 2
value of vec [3] = 3
value of vec [4] = 4
value of v = 0
value of v = 1
value of v = 2
value of v = 3
value of v = 4
集合(Set)是一种包含已排序对象的关联容器。
必须包含的头文件#include <set>
map容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set容器只是单纯的键的集合。当我们想知道某位用户是否存在时,使用set容器是最合适的。
构造、拷贝和析构
操作 | 效果 |
map c | 产生空的map |
map c1(c2) | 产生同类型的c1,并复制c2的所有元素 |
map c(op) | 以op为排序准则产生一个空的map |
map c(beg,end) | 以区间[beg,end]内的元素产生一个map |
map c(beg,end,op) | 以op为排序准则,以区间[beg,end]内的元素产生一个map |
~ map() | 销毁所有元素并释放内存。 |
非变动性操作
操作 | 效果 |
c.size() | 返回元素个数 |
c.empty() | 判断容器是否为空 |
c.max_size() | 返回元素最大可能数量 |
c1==c2 | 判断c1是否等于c2 |
c1!=c2 | 判断c1是否不等于c2 |
c1<c2 | 判断c1是否小于c2 |
c1>c2 | 判断c1是否大于c2 |
c1<=c2 | 判断c1是否大于等于c2 |
c1>=c2 | 判断c1是否小于等于c2 |
赋值
操作 | 效果 |
c1 = c2 | 将c2的全部元素赋值给c1 |
c1.swap(c2) | 将c1和c2的元素互换 |
swap(c1,c2) | 同上,全局函数 |
特殊搜寻操作
操作 | 效果 |
count(key) | 返回”键值等于key”的元素个数 |
find(key) | 返回”键值等于key”的第一个元素,找不到返回end |
lower_bound(key) | 返回”键值大于等于key”的第一个元素 |
upper_bound(key) | 返回”键值大于key”的第一个元素 |
equal_range(key) | 返回”键值等于key”的元素区间 |
迭代器相关函数
操作 | 效果 |
begin() | 返回一个双向迭代器,指向第一个元素 |
end()
| 返回一个双向迭代器,指向最后一个元素之后 |
rbegin() | 返回一个逆向迭代器,指向逆向遍历的第一个元素 |
rend() | 返回一个逆向迭代器,指向逆向遍历的最后一个元素 |
安插元素
操作 | 效果 |
c.insert(e) c.insert(beg,end)
| 插入e的副本,并返回新元素位置 将区间[beg,end]内所有元素的副本插入到c中
|
移除元素
操作 | 效果 |
c.erase(pos) | 删除迭代器pos所指位置的元素,无返回值 |
c.erase(val)
| 移除所有值为val的元素,返回移除元素个数 |
c.erase(beg,end)
| 删除区间[beg,end]内所有元素,无返回值 |
c.clear() | 移除所有元素,清空容器 |
set中不允许key相同的元素,multiset允许key相同的元素
#include <set>
#include <iostream>
using namespace std;
main() {
typedef set<double,less<double> > double_set;
const int SIZE = 5;
double a[SIZE] = {2.1,4.2,9.5,2.1,3.7 };
double_set doubleSet(a,a+SIZE);
ostream_iterator<double> output(cout," ");
cout << "1) ";
copy(doubleSet.begin(),doubleSet.end(),output);
cout << endl;
pair<double_set::const_iterator, bool> p;
p = doubleSet.insert(9.5);
if( p.second )
cout << "2) " << * (p.first) << " inserted" << endl;
else
cout << "2) " << * (p.first) << " not inserted" << endl;
}
//insert函数返回值是一个pair对象, 其first是被插入元素的迭代器,second代表是否成功插入了
输出:
1) 2.1 3.7 4.2 9.5
2) 9.5 not inserted
template<class InIt, class T>
InIt find(InIt first, InIt last, const T& val);
template<class InIt, class Pred>
InIt find_if(InIt first, InIt last, Pred pr);
返回区间 [first,last) 中的迭代器 i, 使得 pr(*i) == true
template<class FwdIt, class T>
bool binary_search(FwdIt first, FwdIt last, const T& val);
上面这个版本,比较两个元素x,y 大小时, 看 x < y
bool binary_search(FwdIt first, FwdIt last, const T& val, Pred pr);
上面这个版本,比较两个元素x,y 大小时, 看 pr(x,y)
#include <vector>
#include <bitset>
#include <iostream>
#include <numeric>
#include <list>
#include <algorithm>
using namespace std;
bool Greater10(int n)
{
return n > 10;
}
main() {
const int SIZE = 10;
int a1[] = { 2,8,1,50,3,100,8,9,10,2 };
vector<int> v(a1,a1+SIZE);
ostream_iterator<int> output(cout," ");
vector<int>::iterator location;
location = find(v.begin(),v.end(),10);
if( location != v.end()) {
cout << endl << "1) " << location - v.begin();
}
location = find_if( v.begin(),v.end(),Greater10);
if( location != v.end())
cout << endl << "2) " << location - v.begin();
sort(v.begin(),v.end());
if( binary_search(v.begin(),v.end(),9)) {
cout << endl << "3) " << "9 found";
}
}
输出:
3) 9 found
template<class FwdIt, class T>
FwdIt lower_bound(FwdIt first, FwdIt last, const T& val);
查找大于等于val的最小的位置
FwdIt upper_bound(FwdIt first, FwdIt last, const T& val);
要求[first,last)是有序的,
查找大于val的最小位置
pair<FwdIt, FwdIt> equal_range(FwdIt first, FwdIt last, const T& val);
返回值是一个pair, 假设为 p, 则
[first,p.first) 中的元素都比 val 小
[p.second,last)中的所有元素都比 val 大
void sort(RanIt first, RanIt last);
按升序排序。判断x是否应比y靠前,就看 x < y 是否为true
void sort(RanIt first, RanIt last, Pred pr);
按升序排序。判断x是否应比y靠前,就看 pr(x,y) 是否为true
FwdIt unique(FwdIt first, FwdIt last);
用 == 比较是否相等
FwdIt unique(FwdIt first, FwdIt last, Pred pr);
用 pr 比较是否等
返回值是迭代器,指向元素删除后的区间的最后一个元素的后面
template<class BidIt>
void reverse(BidIt first, BidIt last);
心得: