重载运算符与STL

重载运算符:使得用户自定义的数据以一种更简洁的方式工作

限制:

不能重载的算符:.    ::    .*    ?:    sizeof

可以重载的运算符 :+     -     *     /     %     ^     &     |     ~

                             !     =     <     >     +=     -=     *=     /=     %   

                            ^=     &=     |=     <<     >>     >>=     <<=     ==     !=    

                            <=     >=     &&     ||     ++     --      ->*     ‘     ->        

                            []     ()      new     delete     new[]     delete[]

重载运算符函数可以对运算符作出新的解释,但原有基本语义不变:

I.不改变运算符的优先级
II.不改变运算符的结合性
III.不改变运算符所需要的操作数
IV.不能创建新的运算符

一元运算符:Object  op    或    op  Object 

二元运算符:ObjectL  op ObjectR

重载为成员函数:

一元:Object . operator op () 操作数由对象Object通过this指针隐含传递
二元:ObjectL . operator op ( ObjectR ) 左操作数由ObjectL通过this指针传递,右操作数由参

成员运算符函数的原型在类的内部声明格式如下:

class X 
{
    //…
返回类型 operator运算符(形参表);
  //…
}
在类外定义成员运算符函数的格式如下:  
返回类型 X::operator运算符(形参表)
{
     函数体
}

重载为友元函数:

一元:operator op (Object)  操作数由参数表的参数Object提供
二元:operator op ( ObjectL, ObjectR )  左右操作数都由参数传递 


双目运算符重载为成员函数:对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。
例:复数加法
#include 
class Complex
{
public:
 Complex( ) {real=0,imag=0;}
 Complex(double r,double i) {real=r; imag=i;}
  Complex operator + (Complex &c2); 
 void display( );
private:
    double real;
    double imag;
};
Complex Complex:: operator + (Complex &c2) {return Complex(real+c2.real, imag+c2.imag);}
void Complex::display( ){cout<<"("<单目运算符重载为成员函数:对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。 
  
例:有一个Time类,包含数据成员minute(分)和sec(秒),模拟秒表,每次走一秒,满60秒进一分钟,此时秒又从0开始算。要求输出分和秒的值。
class Time
{
public:
	Time( ){minute=0;sec=0;}
	Time(int m,int s):minute(m),sec(s){ }
	Time operator++( );     //声明前置自增运算符“++”重载函数
	Time operator++(int);   //声明后置自增运算符“++”重载函数
private:
	int minute;
	int sec;
};
Time Time∷operator++( )    //定义前置自增运算符“++”重载函数
{
	if(++sec>=60)
        {	
		sec-=60;         //满60秒进1分钟
		++minute;
	}
	return *this;          //返回当前对象值
}
Time Time∷operator++(int)  //定义后置自增运算符“++”重载函数
{
	Time temp(*this);
	sec++;
	if(sec>=60)
       {
		sec-=60;
		++minute;
	}
	return temp;         //返回的是自加前的对象
}
注意:一般而言,采用成员函数重载单目运算符时,以下两种方法是等价的:
    @aa;                      // 隐式调用
    aa.operator@();    // 显式调用
    成员运算符函数operator @所需的一个操作数由对象aa通过this指针隐含地传递。因此,在它的参数表中没有参数。

用友元函数重载:

I.在第一个参数需要隐式转换的情形下,使用友元函数重载运算符是正确的选择
II.友元函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
III.C++中不能用友元函数重载的运算符有: =    ()    []    ->
例:复数运算
#include
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)  双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。

重载赋值运算符

I.赋值运算符重载用于对象数据的复制
II.operator= 必须重载为成员函数
III.重载函数原型为:类名  &  类名  :: operator= ( 类名 ) ;
例:定义Name类的重载赋值函数
#include
#include
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( Obj.pName ) + 1 ] ;
   if ( pName != 0 ) strcpy( pName , Obj.pName ) ;
   size = Obj.size ;
   return *this ;
}
Name::~ Name()
{ cout << " Destructing " << pName << endl ;
   delete  []pName ;
   size = 0;
}

重载运算符[]和()
I.运算符 [] 和 () 是二元运算符
II.[] 和 () 只能用成员函数重载,不能用友元函数重载
重载下标运算符 []: [] 运算符用于访问数据对象的元素
                                     重载格式 类型  类 :: operator[]  ( 类型 ) ;


 设 x 是类 X 的一个对象,则表达式
  x [ y ]
 可被解释为
  x . operator [ ] ( y )
例题:
#include
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 
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 ;
}

重载流插入和流提取运算符 :
I.istream 和 ostream 是 C++ 的预定义流类
II.cin 是 istream 的对象,cout 是 ostream 的对象
III.运算符 << 由ostream 重载为插入操作,用于输出基本类型数据
IV.运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据
V.用友元函数重载 << 和 >> ,输出和输入用户自定义的数据类型

重载输出运算符“<<”(只能被重载成友元函数,不能重载成成员函数)
 定义输出运算符“<<”重载函数的一般格式如下:
    ostream& operator<<(ostream& out,class_name& obj)
    {
          out<          out<          .. .
          out<          return out;
    }
重载输入运算符“>>” (只能被重载成友元函数)
定义输入运算符函数 “>>”重载函数的一般格式如下:
    istream& operator>>(istream& in,class_name& obj)
    {
            in>>obj.item1;
            in>>obj.item2;
            . . .
            in>>obj.itemn;
            return in;
    } 
例题:
#include
#include
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 ;
}
vector::vector( int size ) 
{ if (size <= 0 || size > 100 )
    { cout << "The size of " << size << " is null !\n" ; exit( 0 ) ;  }
   v = new int[ size ] ;  len = size ;
}
vector :: ~vector() { delete[] v ;  len = 0 ; }
int & vector :: operator [] ( int i )   
{ if( i >=0 && i < len )  return v[ i ] ;
  cout << "The subscript " << i << " is outside !\n" ;  exit( 0 ) ;
}
ostream & operator << ( ostream & output, vector & ary )
{ for(int i = 0 ; i < ary.len ; i ++ )  output << ary[ i ] << "  " ;
   output << endl ;
   return output ;
}
istream & operator >> ( istream & input, vector & ary ) 
{ for( int i = 0 ; i < ary.len ; i ++ )  input >> ary[ i ] ;
   return  input ;
}
学习心得:
  通过重载运算符的学习,我发现了一种使自己编写的程序变得简洁、高效的方法。通过简单的运算符来取代原来复杂的嵌套,
使程序的简洁性大大提高,写程序的难度也有所降低,但是自己区分起来成员函数和友元也是一个不小的难题。通过自己在ATM
程序中用重载运算符来输入输出,是自己慢慢会用了它,争取以后要熟练运用!

STL

概述:
I.STL是C++标准程序库的核心,深刻影响了标准程序库的整体结构
II.STL由一些可适应不同需求的集合类(collection class),以及在这些数据集合上操作的算法(algorithm)构成
III.STL内的所有组件都由模板(template)构成,其元素可以是任意类型
IV.STL是所有C++编译器和所有操作系统平台都支持的一种库

STL组件
容器(Container) - 管理某类对象的集合
迭代器(Iterator) - 在对象集合上进行遍历
算法(Algorithm) - 处理集合内的元素
容器适配器(container adaptor)
函数对象(functor)

重载运算符与STL_第1张图片
STL之间的协作

 STL容器类别
序列式容器-排列次序取决于插入时机和位置
关联式容器-排列顺序取决于特定准则

重载运算符与STL_第2张图片
STL容器的共同能力
所有容器中存放的都是值而非引用。如果希望存放的不是副本,容器元素只能是指针。
所有元素都形成一个次序(order),可以按相同的次序一次或多次遍历每个元素

STL容器元素的条件
必须能够通过拷贝构造函数进行复制
必须可以通过赋值运算符完成赋值操作
必须可以通过析构函数完称销毁动作
序列式容器元素的默认构造函数必须可用
某些动作必须定义operator ==,例如搜寻操作
关联式容器必须定义出排序准则,默认情况是重载operator <

对于基本数据类型(int,long,char,double,…)而言,以上条件总是满足

重载运算符与STL_第3张图片


与大小相关的操作(size operator)
size()-返回当前容器的元素数量
empty()-判断容器是否为空
max_size()-返回容器能容纳的最大元素数量
比较(comparison)
==,!=,<,<=,>,>=
比较操作两端的容器必须属于同一类型
如果两个容器内的所有元素按序相等,那么这两个容器相等
采用字典式顺序判断某个容器是否小于另一个容器
赋值(assignment)和交换(swap)
swap用于提高赋值操作效率
与迭代器(iterator)相关的操作
begin()-返回一个迭代器,指向第一个元素
end()-返回一个迭代器,指向最后一个元素之后
rbegin()-返回一个逆向迭代器,指向逆向遍历的第一个元素
rend()-返回一个逆向迭代器,指向逆向遍历的最后一个元素之后
元素操作
insert(pos,e)-将元素e的拷贝安插于迭代器pos所指的位置
erase(beg,end)-移除[beg,end]区间内的所有元素
clear()-移除所有元素

迭代器
可遍历STL 容器内全部或部分元素的对象

vector
基本概念:
vector模拟动态元素,元素可以是任意类型
必须包含头文件#include
vector支持随机存取
vector大小size(返回实际元素个数)和容量capacity(返回元素最大数量)

构造、拷贝和析构
重载运算符与STL_第4张图片

非变动操作
重载运算符与STL_第5张图片

赋值操作
重载运算符与STL_第6张图片

元素存取
重载运算符与STL_第7张图片

迭代器相关函数

重载运算符与STL_第8张图片


安插(insert)元素
重载运算符与STL_第9张图片

移除(remove)元素
重载运算符与STL_第10张图片

map/multimap
使用平衡二叉树管理元素
元素包含两部分(key,value),key和value可以是任意类型
必须包含的头文件#include
根据元素的key自动对元素排序,因此根据元素的key进行定位很快,但根据元素的value定位很慢
不能直接改变元素的key,可以通过operator []直接存取元素值
map中不允许key相同的元素,multimap允许key相同的元素

构造、拷贝和析构

重载运算符与STL_第11张图片

非变动性操作

重载运算符与STL_第12张图片

赋值

重载运算符与STL_第13张图片

特殊搜寻操作

重载运算符与STL_第14张图片

迭代器相关函数

重载运算符与STL_第15张图片

安插(insert)元素

重载运算符与STL_第16张图片

移除(remove)元素

重载运算符与STL_第17张图片

set/multiset
使用平衡二叉树管理元素
集合(Set)是一种包含已排序对象的关联容器。
必须包含的头文件#include
map容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set容器只是单纯的键的集合。当我们想知道某位用户是否存在时,使用set容器是最合适的。
set中不允许key相同的元素,multiset允许key相同的元素
重载运算符与STL_第18张图片
重载运算符与STL_第19张图片
例题:

输出:


1) 2.1 3.7 4.2 9.5

2) 9.5 not inserted

#include 
#include 
using namespace std;
main()  {
	typedef set > 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 output(cout," ");
	cout << "1) ";
	copy(doubleSet.begin(),doubleSet.end(),output);
	cout << endl;
pair 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) 0
2) 1
3) (15,2.7),(20,9.3),
4) (15,2.7),(20,9.3),(40,0),
5) (15,6.28),(20,9.3),(40,0),
#include 
#include 
using namespace std;
ostream & operator <<( ostream & o,const pair<  int,double> & p)
{
	o << "(" << p.first  << "," << p.second << ")";
	return o;
}
int main()  {
	typedef map > mmid;
	mmid pairs;
	cout << "1) " << pairs.count(15) << endl;
	pairs.insert(mmid::value_type(15,2.7));
	pairs.insert(make_pair(15,99.3)); //make_pair生成一个pair对象
	cout << "2) " << pairs.count(15) << endl;
	pairs.insert(mmid::value_type(20,9.3));
	mmid::iterator i;
	cout << "3) ";
	for( i = pairs.begin(); i != pairs.end();i ++ )
		cout << * i  << ",";
	cout << endl;
	cout << "4) ";
	int n =  pairs[40];//如果没有关键字为40的元素,则插入一个
	for( i = pairs.begin(); i != pairs.end();i ++ )
		cout << * i  << ",";
	cout << endl;
	cout << "5) ";
	pairs[15] = 6.28; //把关键字为15的元素值改成6.28
	for( i = pairs.begin(); i != pairs.end();i ++ )
		cout << * i  << ",";
}

心得体会:

  STL这个工具非常实用,可以说是前辈们为什么留下的神器,它可以把我们原来需要好几行才能完成的功能用一个函数功能就能代替,例如,循环的语句。这样就可以是我们自己写的程序更加的简明、简洁。但是在实际的使用中,我发现自己定式思维第一时间还是再用原来的方法,没有想到用它的功能,过了半天才想起来,原来这个地方可以用这个函数功能!实际就是自己的 熟练程度还没有达到,还没有将它烂熟于心,希望在今后的学习中,每一个程序都用这么简洁的语言。


你可能感兴趣的:(重载运算符与STL)