一.运算重载符:
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容器。