一. 重载运算符规则
1.重载运算符的限制
C++中大部分运算符都可以被重载,但是以下运算符不能被重载:
. .* :: ?: sizeof
重载运算符可以对运算符内容作出新的解释,但是基本语义不变,如:
- 不改变运算符的优先级
- 不改变运算符的结合性
- 不改变运算符所需要的操作数
注意:不能创建新的运算符,只有系统预定义的运算符才能被重载。
2.重载运算符的语法形式
运算符函数是一种特殊的成员函数或友元函数。成员函数的语句格式为:
类型类名::operator op(参数表)
{
//相对于该类定义的操作
}
其中“类型”是函数的返回值类型。“类名”是要重载该运算符的类。“op”表示要重载的运算符。函数名是“operator op”。“参数表”列出该运算符所需要的操作数。
用于类运算的运算符通常都要重载,但有两个运算符系统提供默认重载版本。
- 赋值运算符“=”,系统默认重载为对象数据成员的复制。
- 地址运算符“&”,系统默认重载为返回任何类对象的地址。
二. 用成员或友元函数重载运算符
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 )
左右操作数都由参数传递。
注意:对双目运算符而言,成员运算符函数的形参表中仅有一个参数,它作为运算符的右操作数,此时当前对象作为运算符的左操作数,它是通过this指针隐含地传递给函数的。
注意:
1.当运算符重载为成员函数时,运算符函数的形式参数的个数比运算符对象个数要少一个。
2.原因是类的非静态成员函数都有一个隐含的this指针,运算符函数可以用this指针隐式地访问类对象的成员,因此这个对象自身的数据可以直接访问,不需要放到形参列表中进行传递,少了的运算对象就是该对象本身。
3.函数的类内声明和类外阐释
- 成员运算符函数的原型在类的内部声明格式如下:
class X {
//…
返回类型 operator运算符(形参表);
//…
}
- 在类外定义成员运算符函数的格式如下:
返回类型 X::operator运算符(形参表)
{
函数体
}
例:
class complex //复数类
{
public:
complex(doubler=0.0,double i=0.0) //构造函数
{real=r;imag=i;}
private:
double real;//实部
double imag;//虚部
};
例题:设计一个复数类,实现对该类对象的加法和减法运算。
#include<iostream>
using namespace std;
class complex
{
public:
complex(double r=0.0,double i=0.0){real=r;imag=i;}
complex operator +(complex &c2);
complex operator -(complex &c2);
void display()
{
cout<<real<<"+"<<imag<<endl;
}
private:
double imag;
double real;
};
complex complex::operator +(complex &c2)
{
complex c;
c.real=c2.real+real;
c.imag=c2.imag+imag;
return c;
}
complex complex::operator -(complex &c2)
{
complex c;
c.real=c2.real-real;
c.imag=c2.imag-imag;
return c;
}
int main()
{
complex c1(1,2),c2(3,4),c3;
c3=c1+c2; c3.display();
c3=c2-c1; c3.display();
return 0;
}
- 一般而言,采用成员函数重载单目运算符时,以下两种方法是等价的:
aa.operator@(); // 显式调用
成员运算符函数operator @所需的一个操作数由对象aa通过this指针隐含地传递。因此,在它的参数表中没有参数。
3.用友元函数重载
- 在第一个参数需要隐式转换的情形下,使用友元函数重载
- 运算符是正确的选择
C++中不能用友元函数重载的运算符有
= () [] ->
4成员运算符函数与友元运算符函数的比较
(1) 成员运算符函数比友元运算符函数少带一个参数(后置的++、--需要增加一个形参)。
(2) 双目运算符一般可以被重载为友元运算符函数或成员运算符函数,但当操作数类型不相同时,必须使用友元函数。
5.前置单目运算符重载为类的成员函数,形式如下:
返回类型 类名::operator op()
{
...... //this指针对应obj运算对象
}
经过重载后,表达式“op obj”
相当于obj.operator op()
例题:实现复数类的前置自增运算
#include<iostream>
using namespace std;
class complex
{
public:
complex(double r=0.0,double i=0.0){real=r;imag=i;}
complex operator ++();
void display()
{
cout<<real<<"+"<<imag<<"i"<<endl;
}
private:
double imag;
double real;
};
complex complex::operator ++()
{
complex a;
real++;
imag++;
a.real=real;
a.imag=imag;
return a;
}
int main()
{
complex c1(1,2),c2;
c2=++c1; c1.display(); c2.display();
return 0;
}
6.后置单目运算符重载为类的成员函数,形式如下:
返回类型 类名::operator op(int)
{
...... //this指针对应obj运算对象
}
经过重载后,表达式“obj op”
相当于obj.operator op(0)
例题:实现复数类的后置自增运算
#include<iostream>
using namespace std;
class complex
{
public:
complex(double r=0.0,double i=0.0){real=r;imag=i;}
complex operator ++(int);
void display()
{
cout<<real<<"+"<<imag<<"i"<<endl;
}
private:
double imag;
double real;
};
complex complex::operator ++()
{
complex a;
a.real=real;
a.imag=imag;
real++;
imag++;
return a;
}
int main()
{
complex c1(1,2),c2;
c2=++c1; c1.display(); c2.display();
return 0;
}
7.当运算符重载为友元函数时,运算符函数的形式参数的个数和运算符uguidingde运算对象个数一致。
形式如下
class 类名{ //类体
...... //友元声明
friend 返回类型 operator 运算符号(形参列表)
};
返回类型 operator 运算符号(形参列表)
{ 函数体 }
8.双目运算符重载为类的友元函数,形式如下:
返回类型 operator op(const 所属类型 &obj1,const 所属类型 &ob1)
{
......//ob1和ob2分别对应两个运算对象
}
经过重载后,表达式“obj1 op ob2”
相当于obj.operator op(obj1,obj2)
二.STL
1. 概述
- STL由一些可适应不同需求的集合类,以及在这些数据集合上操作的算法构成。其所有组件都由模板构成,其元素可以是任意类型。另外,它是一种所有C++编译器和所有操作系统平台都支持的一种库。
- STL组件包括容器(管理某类对象的集合),迭代器(在对象集合上进行遍历),算法,容器适配器,函数对象等。
所有容器中存放的都是值而不是引用,如果希望存放的不是副本,容器元素只能是指针。
STL容器元素的条件
必须能够通过拷贝构造函数进行复制
必须可以通过赋值运算符完成赋值操作
必须可以通过析构函数完称销毁动作
序列式容器元素的默认构造函数必须可用
某些动作必须定义operator ==,例如搜寻操作
关联式容器必须定义出排序准则,默认情况是重载operator <
- 与大小相关的操作(size operator)
empty()-判断容器是否为空
max_size()-返回容器能容纳的最大元素数量
- 比较(comparison)
比较操作两端的容器必须属于同一类型
如果两个容器内的所有元素按序相等,那么这两个容器相等
采用字典式顺序判断某个容器是否小于另一个容器
- 赋值(assignment)和交换(swap)
- 与迭代器(iterator)相关的操作
end()-返回一个迭代器,指向最后一个元素之后
rbegin()-返回一个逆向迭代器,指向逆向遍历的第一个元素
rend()-返回一个逆向迭代器,指向逆向遍历的最后一个元素之后
- 容器的共同操作
insert(pos,e)-将元素e的拷贝安插于迭代器pos所指的位置
erase(beg,end)-移除[beg,end]区间内的所有元素
clear()-移除所有元素
2.迭代器(iterator)(示例:iterator)
可遍历STL容器内全部或部分元素的对象
指出容器中的一个特定位置
迭代器的基本操作
begin() 返回一个迭代器,指向第一个元素
end() 返回一个迭代器,指向最后一个元素之后
- vector
vector的元素可以是任意类型T,但必须具备赋值和拷贝能力(具有public拷贝构造函数和重载的赋值操作符)
必须包含的头文件#include <vector>
vector支持随机存取
vector的大小(size)和容量(capacity)
size返回实际元素个数,
capacity返回vector能容纳的元素最大数量。如果插入元素时,元素个数超过capacity,需要重新配置内部存储器。
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是否大于c2c1<=c2 判断c1是否大于等于c2
c1>=c2 判断c1是否小于等于c2
- vector
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的副本)
- map/multimap
元素包含两部分(key,value),key和value可以是任意类型
必须包含的头文件#include <map>
根据元素的key自动对元素排序,因此根据元素的key进行定位很快,但根据元素的value定位很慢
不能直接改变元素的key,可以通过operator []直接存取元素值
map中不允许key相同的元素,multimap允许key相同的元素
- 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() 销毁所有元素并释放内存。
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(pos,e) 在pos位置为起点插入e的副本,并返回新元素位置(插入速度取决于pos)
c.insert(e) 插入e的副本,并返回新元素位置
c.insert(beg,end) 将区间[beg,end]内所有元素的副本插入到c中
- set/multiset
集合(Set)是一种包含已排序对象的关联容器。
必须包含的头文件#include <set>
map容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set容器只是单纯的键的集合。当我们想知道某位用户是否存在时,使用set容器是最合适的。
set中不允许key相同的元素,multiset允许key相同的元素
begin() 返回指向第一个元素的迭代器
clear() 清除所有元素
count() 返回某个值元素的个数
empty() 如果集合为空,返回true
end() 返回指向最后一个元素的迭代器
equal_range() 返回集合中与给定值相等的上下限的两个迭代器
erase() 删除集合中的元素
find() 返回一个指向被查找到元素的迭代器
get_allocator() 返回集合的分配器
总结:这一章的内容没有很多,对于重载运算符的学习来讲,还是要以前面的内容为基础,这让我感觉到了基础的重要性,这一段时间课程的难度也大,事情也有点多,就多少有些力不从心,我希望在剩下这为数不多的几周里,我要好好学习,然后多拿出点时间来学习,其实说实话,我第一个系统就只得了十分,第一个是不知道哪里有错误,数据都对,但是,从第三个开始,我就卡住了,因为自己学的不扎实,很多东西不会用,就没法写出成功的程序,自己也能觉出来落下了已经,所以我还是自己多看看吧。STL的话呢,如果能熟练掌握将会是很好用的一套模板,在以后的系统中将会起到很大的简化作用。还是那句话,进度落后了还是自己的问题,努力弥补吧,争取早日赶上!