重载运算符与STL学习总结

一.  重载运算符规则

1.重载运算符的限制

C++中大部分运算符都可以被重载,但是以下运算符不能被重载:

 .        .*       ::       ?:       sizeof

  重载运算符可以对运算符内容作出新的解释,但是基本语义不变,如:

  • 不改变运算符的优先级
  • 不改变运算符的结合性
  • 不改变运算符所需要的操作数

注意:不能创建新的运算符,只有系统预定义的运算符才能被重载。

2.重载运算符的语法形式

 运算符函数是一种特殊的成员函数或友元函数。成员函数的语句格式为:

类型类名::operator op(参数表)

{

//相对于该类定义的操作

}

     其中“类型”是函数的返回值类型。“类名”是要重载该运算符的类。“op”表示要重载的运算符。函数名是“operator op”。“参数表”列出该运算符所需要的操作数。

     用于类运算的运算符通常都要重载,但有两个运算符系统提供默认重载版本。

  • 赋值运算符“=”,系统默认重载为对象数据成员的复制。
  • 地址运算符“&”,系统默认重载为返回任何类对象的地址。

二.   用成员或友元函数重载运算符

1.一元运算符

  • 语法形式:Object  opop  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;                      // 隐式调用
    aa.operator@();    // 显式调用

    成员运算符函数operator @所需的一个操作数由对象aa通过this指针隐含地传递。因此,在它的参数表中没有参数。

3.用友元函数重载

  • 在第一个参数需要隐式转换的情形下,使用友元函数重载
  • 运算符是正确的选择
    友元函数没有 this 指针,所需操作数都必须在参数表显式声明,很容易实现类型的隐式转换
 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++编译器和所有操作系统平台都支持的一种库。
2.STL组件
  • STL组件包括容器(管理某类对象的集合),迭代器(在对象集合上进行遍历),算法,容器适配器,函数对象等。
3.STL容器的共同能力

所有容器中存放的都是值而不是引用,如果希望存放的不是副本,容器元素只能是指针。

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

关联式容器必须定义出排序准则,默认情况是重载operator <

  • 与大小相关的操作(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()-移除所有元素

2.迭代器(iterator)(示例:iterator)
可遍历STL容器内全部或部分元素的对象
指出容器中的一个特定位置

迭代器的基本操作

begin()    返回一个迭代器,指向第一个元素
end()     返回一个迭代器,指向最后一个元素之后

  • vector
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是否大于c2
c1<=c2    判断c1是否大于等于c2
c1>=c2   判断c1是否小于等于c2
  • vector
安插(insert)元素
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的话呢,如果能熟练掌握将会是很好用的一套模板,在以后的系统中将会起到很大的简化作用。还是那句话,进度落后了还是自己的问题,努力弥补吧,争取早日赶上!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值