运算符重载
一、运算符重载规则
1、重载运算符的限制
重载运算符函数可以对运算符作出新的解释,但原有基本语义不变:
C++中的运算符除少数几个外,全部可以重载,不能重载的运算符:. 、 :: 、 .*、 ?: 、 sizeof
2、用成员函数重载运算符
成员运算符函数的原型在类的内部声明格式如下:
class X {
//…
返回值类型 operator运算符(形参表);
//…
}
在类外定义成员运算符函数的格式如下:
返回类型 X::operator运算符(形参表)
{
函数体
}
单目运算符重载为成员函数:对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。
一般而言,采用成员函数重载单目运算符时,以下两种方法是等价的:
@aa; // 隐式调用
aa.operator@(); // 显式调用
成员运算符函数operator@所需的一个操作数由对象aa通过this指针隐含地传递。因此,在它的参数表中没有参数。
双目运算符重载为成员函数:对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。
一般而言,如果在类X中采用成员函数重载双目运算符@,成员运算符函数operator@所需的一个操作数由对象aa通过this指针隐含地传递,它的另一个操作数bb在参数表中显示,aa和bb是类X的两个对象,则以下两种函数调用方法是等价的:
aa @bb; // 隐式调用
aa.operator @(bb); // 显式调用
一元运算符的操作数以及二元运算符的左操作数通过this指针传递
3、用友元函数重载运算符
= () [] ->
例:复数运算
#include<iostream>
using namespace std;
class Complex
{ public:
Complex( double r =0, double i =0 ) { Real = r ; Image = i ; }
Complex(int a) { Real = a ; Image = 0 ; }
void print() const ;
friend Complex operator+ ( const Complex & c1, const Complex & c2 ) ;
friend Complex operator- ( const Complex & c1, const Complex & c2 ) ;
friend Complex operator- ( const Complex & c ) ;
private:
double Real, Image ;
};
Complex operator + ( const Complex & c1, const Complex & c2 )
{ double r = c1.Real + c2.Real ; double i = c1.Image+c2.Image ;
return Complex ( r, i ) ;
}
Complex operator - ( const Complex & c1, const Complex & c2 )
{ double r = c1.Real - c2.Real ; double i = c1.Image - c2.Image ;
return Complex ( r, i ) ;
}
Complex operator- ( const Complex & c )
{ return Complex ( -c.Real, - c.Image ) ; }
void Complex :: print() const
{ cout << '(' << Real << " , " << Image << ')' << endl ; }
成员运算符函数与友元运算符函数的比较:
(1) 成员运算符函数比友元运算符函数少带一个参数(后置的++、--需要增加一个形参)。
(2) 双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。
3、几个典型运算符重载
重载赋值运算符
类名 & 类名 :: operator= ( 类名) ;
#include<iostream>
#include<cstring>
using namespace std;
class Name
{ public :
Name ( char *pN ) ;
Name( const Name & ) ; //复制构造函数
Name& operator=( const Name& ) ; // 重载赋值运算符
~ Name() ;
protected :
char *pName ;
int size ;
} ;
int main()
{ Name Obj1( "ZhangSan" ) ;
Name Obj2 = Obj1 ; // 调用复制构造函数
Name Obj3( "NoName" ) ;
Obj3 = Obj2 = Obj1 ; // 调用重载赋值运算符函数
}
Name::Name ( char *pN )
{ cout <<" Constructing " << pN << endl ;
pName = new char[ strlen( pN ) + 1 ] ;
if( pName != 0 ) strcpy( pName,pN ) ;
size = strlen( pN ) ;
}
Name::Name( const Name & Obj ) //复制构造函数
{ cout << " Copying " << Obj.pName << " into its own block\n";
pName = new char[strlen( Obj.pName ) + 1 ] ;
if ( pName != 0 ) strcpy( pName, Obj.pName ) ;
size = Obj.size;
}
Name & Name::operator= ( const Name & Obj ) // 重载赋值运算符
{ delete []pName ;
pName = new char[ strlen
Name & Name::operator= ( const Name & Obj )
{ delete []pName ;
pName = new char[ strlen( Obj.pName ) + 1 ] ;
if ( pName != 0 ) strcpy( pName , Obj.pName ) ;
size = Obj.size ;
return *this ;
}
重载运算符[]和()
[]运算符用于访问数据对象的元素
重载格式 类型 类::operator[] ( 类型) ;
例
设 x 是类 X 的一个对象,则表达式
x[ y ]
可被解释为
x . operator [ ] ( y )
#include<iostream>
using namespace std;
class vector
{ public :
vector ( int n ) { v = new int [ n ] ; size = n ; }
~ vector ( ) { delete [ ] v ; size = 0 ; }
int & operator [ ] ( int i ) { return v [ i ] ; }
private :
int * v ; int size ;
};
int main ( )
{ vector a ( 5 ) ;
a [ 2 ] = 12 ;
cout << a [ 2 ] << endl ;
}
()运算符用于函数调用
重载格式 类型 类:: operator() ( 参数表 ) ;
例
设 x是类 X的一个对象,则表达式
x ( arg1, arg2, … )
可被解释为
x . operator () (arg1, arg2, … )
#include <iostream>
using namespace std ;
class F
{ public :
double operator ( ) ( double x , double y ) ;
} ;
double F :: operator ( ) ( double x , double y )
{ return x * x + y * y ; }
int main ( )
{ F f ;
cout << f ( 5.2 , 2.5 ) << endl ;
}
重载流插入和流提取运算符
定义输出运算符“<<”重载函数的一般格式如下:
ostream&operator<<(ostream&out,class_name&obj)
{
out<<obj.item1;
out<<obj.item2;
.. .
out<<obj.itemn;
return out;
}
重载输入运算符“ >>” (只能被重载成友元函数)定义输入运算符函数“>>”重载函数的一般格式如下:
istream&operator>>(istream&in,class_name&obj)
{
in>>obj.item1;
in>>obj.item2;
. . .
in>>obj.itemn;
return in;
}
#include<iostream>
#include<cstdlib>
using namespace std;
class vector
{ public :
vector( int size =1 ) ; ~vector() ;
int & operator[] ( int i ) ;
friend ostream & operator << ( ostream & output , vector & ) ;
friend istream & operator >> ( istream & input, vector & ) ;
private :
int * v ; int len ;
};
int main(){
int k ; cout << "Input the length of vector A :\n" ; cin >> k ;
vector A( k ) ; cout << "Input the elements of vector A :\n" ;
cin >> A ; cout << "Output the elements of vector A :\n" ;
cout << A ;
}
STL(标准模板库)
一、STL组件
1、容器
容器类别
容器的共同能力
容器元素要满足的条件
容器的共同操作
rend()-返回一个逆向迭代器,指向逆向遍历的最后一个元素之后
二、迭代器
迭代器的基本操作
*:返回当前位置上的元素值。如果该元素有成员,可以通过迭代器以operator->取用
++:将迭代器前进至下一元素
==和!=:判断两个迭代器是否指向同一位置
=:为迭代器赋值(将所指元素的位置赋值过去)
所有容器都提供的获得迭代器的函数:
begin()返回一个迭代器,指向第一个元素
end() 返回一个迭代器,指向最后一个元素之后
半开区间[beg, end)的好处:
1.为遍历元素时循环的结束时机提供了简单的判断依据(只要未到达end(),循环就可以继续)
2.不必对空区间采取特殊处理(空区间的begin()就等于end())
迭代器的分类
list<int> l; //双向迭代器
for(pos=l.begin();pos!=l.end();++pos{
…
}
vector<int> v; //随机存取迭代器
for(pos=v.begin();pos<v.end();++pos{
…
}
3、STL中常见的几种容器
(1)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]为元素初值 |
~vector<T>() | 销毁所有元素并释放内存。 |
非变动操作
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() | 返回最后一个元素的引用,不检查元素是否存在 |
安插元素
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的副本) |
(2)map/multimap
内部存储结构:
拷贝、构造、析构函数操作
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() | 销毁所有元素并释放内存。 |
其中map可以是下列形式
map<key,value> 一个以less(<)为排序准则的map,
map<key,value,op> 一个以op为排序准则的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”的元素区间 |
安插元素:
c.insert(pos,e) | 在pos位置为起点插入e的副本,并返回新元素位置(插入速度取决于pos) |
c.insert(e) | 插入e的副本,并返回新元素位置 |
c.insert(beg,end) | 将区间[beg,end]内所有元素的副本插入到c中 |
移除元素:
c.erase(pos) | 删除迭代器pos所指位置的元素,无返回值 |
c.erase(val) | 移除所有值为val的元素,返回移除元素个数 |
c.erase(beg,end) | 删除区间[beg,end]内所有元素,无返回值 |
c.clear() | 移除所有元素,清空容器 |
(3)set/multiset
返回指向第一个元素的迭代器 | |
清除所有元素 | |
返回某个值元素的个数 | |
如果集合为空,返回true | |
返回指向最后一个元素的迭代器 | |
返回集合中与给定值相等的上下限的两个迭代器 | |
删除集合中的元素 | |
返回一个指向被查找到元素的迭代器 | |
返回集合的分配器 |
在集合中插入元素 | |
返回指向大于(或等于)某值的第一个元素的迭代器 | |
返回一个用于元素间值比较的函数 | |
返回集合能容纳的元素的最大限值 | |
返回指向集合中最后一个元素的反向迭代器 | |
返回指向集合中第一个元素的反向迭代器 | |
集合中元素的数目 | |
交换两个集合变量 | |
返回大于某个值元素的迭代器 | |
返回一个用于比较元素间的值的函数 |
pair模板:pair模板可以用于生成key-value对