初学C++运算重载符和STL总结

一.运算重载符:

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

例如:

int  x , y	;
y = x + y ; 
complex  c1 , c2 ;  		// 复数类对象
    	      // 调用函数计算两个复数的和
matrix  m1 , m2 ;		// 矩阵类对象	
                	// 调用函数计算两个矩阵的和

2.运算重载符的限制:

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

可以重载的算符:

+ - * / % ^ & | ~
! = < > += -= *= /= %
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- ->* ->

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

重载运算符函数可以对运算符作出新的解释,但原有基本语义不变:
不改变运算符的优先级
不改变运算符的结合性
不改变运算符所需要的操作数 

不能创建新的运算符且运算符函数可以重载为成员函数或友元函数 

3.分类:

(1)一元运算符:Object  op    或    op  Object

重载为成员函数,解释为:
Object . operator op ()
操作数由对象Object通过this指针隐含传递
重载为友元函数,解释为:
operator op (Object)
    操作数由参数表的参数Object提供 

(2)二元运算符:ObjectL  op ObjectR

重载为成员函数,解释为:
ObjectL . operator op ( ObjectR )
    左操作数由ObjectL通过this指针传递,右操作数由参数ObjectR传递 
重载为友元函数,解释为:
operator op ( ObjectL, ObjectR )
  左右操作数都由参数传递 

4.用成员函数重载运算符:

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

(1)对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。 

例如:

#include <iostream.h>
class Complex
{
public:
	Complex( )	{real=0,imag=0;}
	Complex(double r,double i)	{real=r; imag=i;}
	 Complex operator + (Complex &c2); 
	void display( );
Complex Complex:: operator + (Complex &c2) {
	return Complex(real+c2.real, imag+c2.imag);}
void Complex::display( ){
	cout<<"("<<real<<","<<imag<<"i)"<<endl;}
int main( ){
	Complex c1(3,4),c2(5,-10),c3;
	c3=c1+c2;
	cout<<"c1=";c1.display( );
	cout<<"c2=";c2.display( );
	cout<<"c1+c2 =";	c3.display( );
	return 0;
}
一般而言,如果在类X中采用成员函数重载双目运算符@,成员运算符函数operator@ 所需的一个操作数由对象aa通过this指针隐含地传递,它的另一个操作数bb在参数表中显示,aa和bb是类X的两个对象,则以下两种函数调用方法是等价的:
    aa @ bb;                      // 隐式调用

    aa.operator @(bb);     // 显式调用

(2)对单目运算符而言,成员运算符函数的参数表中没有参数,此时当前对象作为运算符的一个操作数。

例如:有一个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指针隐含地传递。因此,在它的参数表中没有参数。

5.用友元函数重载:

在第一个参数需要隐式转换的情形下,使用友元函数重载
    运算符是正确的选择;
 友元函数没有 this 指针,所需操作数都必须在参数表显式
    声明,很容易实现类型的隐式转换;
 C++中不能用友元函数重载的运算符有

=    ()    []    ->

例如:复数运算

 #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)  双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。

6.重载赋值运算符:

赋值运算符重载用于对象数据的复制 
operator= 必须重载为成员函数 
重载函数原型为:

类名  &  类名  :: operator= ( 类名 ) ; 

例如:定义Name类的重载赋值函数

#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 ;		// 调用重载赋值运算符函数 
}

7.重载运算符[]和():

运算符 [] 和 () 是二元运算符

[] 和 () 只能用成员函数重载,不能用友元函数重载 

(1)重载下标运算符 []:

[] 运算符用于访问数据对象的元素;

重载格式 类型  类 :: operator[]  ( 类型 ) ;

例如:

#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 ;
}

(2)重载函数调用符 ():

() 运算符用于函数调用;

重载格式 类型  类 :: operator()  ( 参数表  ) ;

例如:

#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 ;
}

8.重载流插入和流提取运算符:

istream 和 ostream 是 C++ 的预定义流类;
cin 是 istream 的对象,cout 是 ostream 的对象;
运算符 << 由ostream 重载为插入操作,用于输出基本类型数据;
运算符 >> 由 istream 重载为提取操作,用于输入基本类型数据;

用友元函数重载 << 和 >> ,输出和输入用户自定义的数据类型 。

(1)重载输出运算符“<<”(只能被重载成友元函数,不能重载成成员函数):

定义输出运算符“<<”重载函数的一般格式如下

ostream& operator<<(ostream& out,class_name& obj)
    {
          out<<obj.item1;
          out<<obj.item2;
          .. .
          out<<obj.itemn;
          return out;
    } 

(2)重载输入运算符“>>” (只能被重载成友元函数):

定义输入运算符函数 “>>”重载函数的一般格式如下

 istream& operator>>(istream& in,class_name& obj)
    {
            in>>obj.item1;
            in>>obj.item2;
            . . .
            in>>obj.itemn;
            return in;}

二.STL(C++标准模板库):

1.概述:

STL是C++标准程序库的核心,深刻影响了标准程序库的整体结构;
STL由一些可适应不同需求的集合类(collection class),以及在这些数据集合上操作的算法(algorithm)构成;
STL内的所有组件都由模板(template)构成,其元素可以是任意类型;

STL是所有C++编译器和所有操作系统平台都支持的一种库。

2.组件:

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

函数对象(functor) 。

3.STL容器的共同操作:

(1)初始化(initialization):

产生一个空容器

std::list<int> l;

以另一个容器元素为初值完成初始化

std::list<int> l;
…
std::vector<float> c(l.begin(),l.end());

以数组元素为初值完成初始化

int array[]={2,4,6,1345};
…
std::set<int> c(array,array+sizeof(array)/sizeof(array[0]));

(2)与大小相关的操作(size operator):

size()-返回当前容器的元素数量
empty()-判断容器是否为空

max_size()-返回容器能容纳的最大元素数量

(3)比较(comparison):

==,!=,<,<=,>,>=
比较操作两端的容器必须属于同一类型;
如果两个容器内的所有元素按序相等,那么这两个容器相等;

采用字典式顺序判断某个容器是否小于另一个容器。

(4)赋值(assignment)和交换(swap):

swap用于提高赋值操作效率

(5)与迭代器(iterator)相关的操作:

begin()-返回一个迭代器,指向第一个元素
end()-返回一个迭代器,指向最后一个元素之后
rbegin()-返回一个逆向迭代器,指向逆向遍历的第一个元素

rend()-返回一个逆向迭代器,指向逆向遍历的最后一个元素之后

(6)元素操作:
insert(pos,e)-
将元素e的拷贝安插于迭代器pos所指的位置
erase(beg,end)-移除[beg,end]区间内的所有元素

clear()-移除所有元素

4.迭代器:

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

指出容器中的一个特定位置。

---基本操作:

    返回当前位置上的元素值。如果该元素有成员,可以通过迭代器以operator ->取用;

++   将迭代器前进至下一元素;

==和!=  判断两个迭代器是否指向同一位置;

=   为迭代器赋值;

5.重点容器:

(1)vector:

vector模拟动态数组;
vector的元素可以是任意类型T,但必须具备赋值和拷贝能力(具有public拷贝构造函数和重载的赋值操作符);
必须包含的头文件#include <vector>;
vector的大小(size)和容量(capacity);
size返回实际元素个数;
capacity返回vector能容纳的元素最大数量。如果插入元素时,元素个数超过capacity,需要重新配置内部存储器。

(2)map/multimap:

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

map中不允许key相同的元素,multimap允许key相同的元素。

内部存储结构:


(3)set/multiset:

使用平衡二叉树管理元素;
集合(Set)是一种包含已排序对象的关联容器;
必须包含的头文件#include <set>;
map容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set容器只是单纯的键的集合。当我们想知道某位用户是否存在时,使用set容器是最合适的;

set中不允许key相同的元素,multiset允许key相同的元素。

三.学习心得:

首先,运算重载符的掌握是我体会到了把多个相似的函数合并后,整个程序变得简洁,而且编码效率提高了,利于程序的纠错和优化,并且减少了一些动态类型的混乱,从这里开始,我开始真正了解到了C++的魅力;而STL提供的各类容器及其作用进一步优化的程序编码的效率,使得程序更加简洁高级,但是其巨大的容器量是我尚未掌握的,我学到的只是冰山一角,所以接下来的学习要尽可能多的学习更多的STL容器。

猜你喜欢

转载自blog.csdn.net/fouram_godv/article/details/80470541