5.2.5 "++"和"--"的重载

 对于前缀方式++ob,可以用运算符函数重载为:
ob.operator++() //成员函数重载

operator++(X &ob) //友元函数重载,其中ob为类X的对象的引用 对于后缀方式++ob,可以用运算符函数重载为:
ob.operator++(int) //成员函数重载

operator++(X &ob,int) //友元函数重载,其中ob为类X的对象的引用 调用时,参数int一般被传递给值0,例如:
class X{
...
public:
...
X operator++(); //前缀方式
X operator++(int); //后缀方式
};
int main()
{
X ob;
...
++ob; //隐式调用ob.operator++()
ob++; //隐式调用ob.operator++(int)
ob.operator++(); //显式调用ob.operator++(),意为++ob
ob.operator++(); //显式调用ob.operator++(int),意为ob++
} 类似的,也可以重载为友元函数,例如: class Y{
...
public:
...
firend Y operator++(Y &); //前缀方式
friend Y operator++(Y &,int); //后缀方式
};
int main()
{
Y ob;
...
++ob; //隐式调用ob.operator++(Y&)
ob++; //隐式调用ob.operator++(Y&,int)
operator++(ob); //显式调用ob.operator++(Y&),意为++ob
operator++(ob,); //显式调用ob.operator++(Y&,int),意为ob++
}

//例5.8 使用成员函数以前缀方式后后缀方式重载运算符"--"

#include<iostream>
using namespace std;
class Three{
public:
Three(int I1=,int I2=,int I3=)
{
i1 = I1;
i2 = I2;
i3 = I3;
}
Three operator--(); //声明自减运算符--重载成员函数(前缀方式)
Three operator--(int a); //声明自减运算符--重载成员函数(后缀方式)
void show();
private:
int i1,i2,i3;
};
Three Three::operator--() //定义自减运算符--重载成员函数(前缀方式)
{
--i1;
--i2;
--i3;
return *this; //返回自减后的当前对象
}
Three Three::operator--(int a) //定义自减运算符--重载成员函数(后缀方式)
{
Three temp(*this);
i1--;
i2--;
i3--;
return temp; //返回自减前的当前对象
}
void Three::show()
{
cout<<"i1="<<i1<<",";
cout<<"i2="<<i2<<",";
cout<<"i3="<<i3<<endl;
}
int main()
{
Three t1(,,),t2,t3(,,),t4;
t1.show(); --t1; //隐式调用(前缀方式)
t1.show(); //显示执行--t1后的值
t2=t1--; //隐式调用(后缀方式),将t1自减前的值赋给t2
t2.show(); //显示t2保存的是执行t1--之前的t1的值
t1.show(); //显示执行t1--之后的t1的值
cout<<endl; t3.show();
t3.operator--(); //显示调用(前缀方式)
t3.show();
t4=t3.operator--(); //显示调用(后缀方式)
t4.show();
t3.show(); return ;
}

//例5.8 使用友元函数以前缀方式和后缀方式重载运算符"++"

#include<iostream>
using namespace std;
class Three{
public:
Three(int I1=,int I2=,int I3=)
{
i1 = I1;
i2 = I2;
i3 = I3;
}
friend Three operator++(Three &); //声明自加运算符++重载成员函数(前缀方式)
friend Three operator++(Three &,int ); //声明自加运算符++重载成员函数(后缀方式)
void show();
private:
int i1,i2,i3;
};
Three operator++(Three &T) //声明自加运算符++重载友元函数(前缀方式)
{
++T.i1;
++T.i2;
++T.i3;
return T; //返回自加后的对象
}
Three operator++(Three &T,int a) //声明自加运算符--重载友元函数(后缀方式)
{
Three temp(T);
T.i1++;
T.i2++;
T.i3++;
return temp; //返回自加前的对象
}
void Three::show()
{
cout<<"i1="<<i1<<",";
cout<<"i2="<<i2<<",";
cout<<"i3="<<i3<<endl;
}
int main()
{
Three t1(,,),t2,t3(,,),t4;
t1.show(); ++t1; //隐式调用(前缀方式)
t1.show(); //显示执行++t1后的值
t2=t1++; //隐式调用(后缀方式),将t1自加前的值赋给t2
t2.show(); //显示t2保存的是执行t1++之前的t1的值
t1.show(); //显示执行t1++之后的t1的值
cout<<endl; t3.show();
operator++(t3); //显示调用(前缀方式)
t3.show();
t4=operator++(t3,); //显示调用(后缀方式)
t4.show();
t3.show(); return ;
}

说明:

(1)由于友元运算符重载函数没有this指针,所以不能引用this指针所指的对象,使用友元函数重载增运算符"++"或自减"--"时,应采用对象引用传递数据。

例如:
friend Three operator++(Three &); //声明自加运算符++重载成员函数(前缀方式)
friend Three operator++(Three &,int ); //声明自加运算符++重载成员函数(后缀方式)

(2)前缀方式和后缀方式的函数内部语句可以相同,也可以不同,取决于编程的需要。

5.2.7 下标运算符"[]"的重载
在C++中,在重载下标运算符[]时,认为它是一个双目运算符,例如X[Y]可以看成:
[]---------双目运算符
X----------左操作数
Y----------右操作数
其相应的运算符重载函数名为operator[]。
设X是一个类的对象,类中定义了重载"[]"的operator[]函数,则表达式
  X[Y]   被解释为    X.operator[](y);

下标运算符重载函数只能定义成员函数,其形式如下:
返回类型 类名::operator[](形参)

      //函数体
 }

注意:形参在此表示下标,C++规定只能有一个参数

//例5.12 使用下标运算符重载函数的引例

#include<iostream>
using namespace std;
class Vector4{
public:
Vector4(int a1,int a2,int a3,int a4)
{
v[]=a1;
v[]=a2;
v[]=a3;
v[]=a4;
}
private:
int v[];
};
int main()
{
Vector4 ve(,,,);
cout<<v[]; //运行错误,v[2]是类Vector4的私有成员,即使是公有成员,输出格式应为ve.v[2];
return ;
} 可是,如果对[]进行重载,即使v[]是私有成员,也可以运行成功,直接访问。即:
int &Vector::operator[](int bi)
{
if(bi<||bi>=)
{
cout<<"Bad subscript!\n";
exit();
}
return v[bi]; //v[bi]被解释为v.operator[](2)
}

//例5.13 使用下标运算符[]重载函数

#include<iostream>
using namespace std;
class Vector4{
public:
Vector4(int a1,int a2,int a3,int a4)
{
v[]=a1;
v[]=a2;
v[]=a3;
v[]=a4;
}
int &operator[](int );
private:
int v[];
};
int &Vector4::operator[](int bi) //返回一个int型的引用
{
if(bi<||bi>=)
{
cout<<"Bad subscript!\n";
exit();
}
return v[bi]; //v[bi]被解释为v.operator[](2)
}
int main()
{
Vector4 v(,,,);
int i=;
for(;i<;i++)
cout<<"v["<<i<<"]="<<v[i]<<"\n"; //v[i]被解释为v.operator[](i);
cout<<endl; v[]=v[]; //v[2]被解释为v.operator[](2);
cout<<"v[3]="<<v[]<<endl; //v[3]被解释为v.operator[](3); v[]=;
cout<<"v[2]="<<v[]<<endl; return ;
}
/*
运行结果:
v[0]=1
v[1]=2
v[2]=3
v[3]=4 v[3]=3
v[2]=22
*/

5.2.6 赋值运算符"="的重载
对于任一类X,如果没有用户自定义的赋值运算符函数,那么系统将自动地为其生成一个默认的
赋值运算符函数,例如:
X &X::operator=(const X &source)
{
//成员间赋值
}
若obj1和obj2是类X的两个对象,obj2已经建立,则编译程序遇到如下语句;
obj1=obj2;
就调用默认的赋值运算符函数,将对象obj2的数据成员逐域复制到obj1中。

采用默认的赋值运算符函数实现的数据成员逐一赋值的方法是一种浅层复制非方法。通常,默认的赋值运算符函数是能够胜任工作的。但是,对于许多重要的实例类来说,仅有默认的赋值运算符函数还是不够的,还需要用户根据实际需要自己对赋值元算法进行重载,以解决遇到的问题。指针悬挂就是这方面的一个典型问题。

1.指针悬挂问题
在某些特殊情况下,如类中有指针类型时,使用默认的赋值运算符函数会产生错误。
//例 5.10 关于浅层复制的例子。

#include<iostream>
using namespace std;
class STRING{
public:
STRING(char* s)
{
cout<<"Constructor called."<<endl;
pt = new char(strlen(s)+);
strcpy(pt,s);
}
~STRING()
{
cout<<"Destructor called."<<pt<<endl;
delete pt;
}
private:
char* pt;
};
int main()
{
STRING p1("book");
STRING p2("jeep");
p2=p1;
return ;
}

运行结果:
Constructor called. (1)
Constructor called. (2)
Destructor called.book (3)
Destructor called.*q (4)

结果出现了指针悬挂问题。

原因分析:对象p1和p2建立时,分别调用构造函数,输出(1)(2)。通过new运算符分别从内存动态分配一块空间,对象p1的字符指针pt指向book,对象p2的字符指针pt指向jeep;执行p2=p1时,由于用户没有定义赋值运算符函数,系统于是就会调用默认的赋值运算法函数。使对象p1和p2的字符指针pt都指向new开辟的同一块内存空间,该内存空间里所存放的内容是book;

主程序结束时,系统逐一撤销建立的对象,因此第一次调用析构函数,撤销对象p2,输出(3),并用delete释放new开辟的动态空间。再进行第二次调用析构函数,撤销对象p1,由于p1和p2的指针pt是指向同一内存的,在撤销p2时,已经释放了pt指向的空间,此时,尽管对象p1的指针pt存在,可是却无法访问此空间了。所以输出的(4)中pt指向的内容是随机字符,而不是book.同一空间当是不允许用delete释放两次的,这就是所谓的指针悬挂问题。

由于本例的类中含有指向动态空间的指针pt,执行语句"p2=p1"时,调用的就是默认的赋值运算符函数,采用的是浅层复制方法,使两个对象p1和p2的指针pt都指向new开辟的同一个空间,于是出现了指针悬挂现象。

2.用深层复制解决指针悬挂问题
为了解决浅层复制出现的错误,必须显示地定义一个自己的赋值运算符重载函数,使之不但复制数据成员,而且为对象p1和p2分配了各自的内存空间,这就是所谓的深层复制。

//例5.11 关于深层赋值的例子

#include<iostream>
using namespace std;
class STRING{
public:
STRING(char* s)
{
cout<<"Constructor called."<<endl;
pt = new char(strlen(s)+);
strcpy(pt,s);
}
STRING &operator=(const STRING &s); //声明赋值运算符重载函数
~STRING()
{
cout<<"Destructor called."<<pt<<endl;
delete pt;
}
private:
char* pt;
};
STRING &STRING::operator=(const STRING &s) //定义赋值运算符重载函数
{
if(this==&s) return *this; //防止s=s的赋值
delete pt; //释放掉原区域
pt = new char(strlen(s.pt)+); //分配新区域
strcpy(pt,s.pt); //字符串复制
return *this;
}
int main()
{
STRING p1("book");
STRING p2("jeep");
p2=p1;
return ;
}

运行结果:
Constructor called.
Constructor called.
Destructor called.book
Destructor called.book

结果解决了指针悬挂问题。
原因分析:对象p1和p2建立时,分别调用构造函数,输出(1)(2)。通过new运算符分别从内存动态分配一块空间,对象p1的字符指针pt指向book,对象p2的字符指针pt指向jeep;执行p2=p1时,由于用户自己定义了赋值运算符函数,释放掉了p2指针pt所指的旧区域,又按照新的长度分配新的内存空间给p2,再把对象p1的指针pt所指向的数据book赋给p2对象的指针pt所指向的区域内。也即 p1的指针pt指向book,p2的指针pt也指向book。

主程序结束时,系统逐一撤销建立的对象,虽然对象p1和对象p2的指针pt都指向了相同内容book,但是它们却分别有自己的动态分配的空间。所以delete释放空间时,就不会出现指针悬挂现象了。

说明:类的赋值运算符"="只能重载为成员函数,而不能把它重载为友元函数,因为若把上述赋值运算符"="重载为友元函数

friend string &operator=(string &p2,string &p1)

表达式
p1="book" 将被解释为 operator=(p1,book) 这显然是没有问题的,

但是对于表达式"book"=p1 将被解释为 operator=(book,p1),即C++编译器首先将book转换成一个隐藏的string对象,然后使用对象p2引用该隐藏的对象,并不认为这个表达式是错的,从而导致赋值语句上的混乱。因此,双目赋值运算符重载为成员函数,而不能重载为友元函数。

C++:运算符重载函数之"++"、"--"、"[ ]"、"=="的应用的更多相关文章

  1. C++:成员运算符重载函数和友元运算符重载函数的比较

    5.2.4 成员运算符重载函数和友元运算符重载函数的比较 (1)对双目运算符而言,成员运算符重载函数参数表中含有一个参数,而友元运算符重载函数参数表中有两个参数:对于单目运算符而言,成员运算符重载函数 ...

  2. C++:运算符重载函数之成员运算符重载函数

    5.2.3 成员运算符重载函数 在C++中可以把运算符重载函数定义为某个类的成员函数,称之为成员运算符重载函数. 1. 定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式 ...

  3. C++:运算符重载函数之友元运算符重载

    5.2.2 友元运算符重载函数 运算符重载函数一般采用两种形式定义: 一是定义为它将要操作的类的成员函数(简称运算符重载函数): 二是定义为类的友元函数(简称为友元运算符重载函数). 1.定义友元运算 ...

  4. C++:运算符重载函数

    5.运算符重载 5.1 在类外定义的运算符重载函数 C++为运算符重载提供了一种方法,即在运行运算符重载时,必须定义一个运算符重载函数,其名字为operator,后随一个要重载的运算符.例如,要重载& ...

  5. C++:友元运算符重载函数

    运算符重载函数:实现对象之间进行算数运算,(实际上是对象的属性之间做运算),包括+(加号).-(减号).*./.=.++.--.-(负号).+(正号) 运算符重载函数分为:普通友元运算符重载函数.成员 ...

  6. C++ 类型转化(运算符重载函数)和基本运算符重载(自增自减)

    类型转化(运算符重载函数) 用转换构造函数可以将一个指定类型的数据转换为类的对象.但是不能反过来将一个类的对象转换为一个其他类型的数据(例如将一个Complex类对象转换成double类型数据).在C ...

  7. 《挑战30天C++入门极限》C++中利用构造函数与无名对象简化运算符重载函数

        C++中利用构造函数与无名对象简化运算符重载函数 在完整描述思想之前,我们先看一下如下的例子,这个例子中的加运算符重载是以非成员函数的方式出现的: //程序作者:管宁  //站点:www.cn ...

  8. 《挑战30天C++入门极限》C++运算符重载函数基础及其值返回状态

        C++运算符重载函数基础及其值返回状态 运算符重载是C++的重要组成部分,它可以让程序更加的简单易懂,简单的运算符使用可以使复杂函数的理解更直观. 对于普通对象来说我们很自然的会频繁使用算数运 ...

  9. swift:高级运算符(位运算符、溢出运算符、优先级和结合性、运算符重载函数)

    swift:高级运算符 http://www.cocoachina.com/ios/20140612/8794.html 除了基本操作符中所讲的运算符,Swift还有许多复杂的高级运算符,包括了C语和 ...

随机推荐

  1. 鼠标驱动之-sys节点-input子系统

    首先需要了解sys节点和linux驱动编程的知识,在linux内核<linux/>下有着对应的实现.本例实现创建sys节点,外围程序通过input子系统控制鼠标位置. 第一步编写驱动代码, ...

  2. c++数组-矩阵的转置

    #include <iostream> using namespace std; int main(){ ][]={{,,},{,,}}; ][]; ;j<;j++){ ;i< ...

  3. StackExchange.Redis的使用

    StackExchange.Redis介绍 有需要了解的和基础的使用可以参考:http://www.cnblogs.com/bnbqian/p/4962855.html StackExchange.R ...

  4. matlab实现高斯牛顿法、Levenberg–Marquardt方法

    高斯牛顿法: function [ x_ans ] = GaussNewton( xi, yi, ri) % input : x = the x vector of 3 points % y = th ...

  5. Careercup - Google面试题 - 4557716425015296

    2014-05-03 21:57 题目链接 原题: Many sticks with length, every time combine two, the cost is the sum of tw ...

  6. 如何解决Mac与iPhone之间handoff连接问题

    首先账户以及设备handoff开关问题不再赘述.主要是昨天发现的一个小技巧 当确认所有设备的iCloud账号统一.蓝牙打开.处在同一WiFi下的前提下,我的iPhone和Mac仍然handoff连接有 ...

  7. ios7去除手势滑动返回

    if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { sel ...

  8. 11个实用jQuery日历插件

    1. FullCalendar FullCalendar是很出名的jQuery日历插件,它支持拖拽等功能,整合了Google Calendar,而且可以通过JSON来绑定事件,设计师可以轻松地自定义日 ...

  9. 使用shell脚本获取虚拟机中cpu使用率(读/proc/statc)

    #!/bin/bash interval= cpu_num=`-] -c` start_idle=() start_total=() cpu_rate=() cpu_rate_file=./`host ...

  10. DataGridView之行的展开与收缩

    很多数据都有父节点与子节点,我们希望单击父节点的时候可以展开父节点下的子节点数据. 比如一个医院科室表,有父科室与子科室,点击父科室后,在父科室下面可以展现该科室下的所有子科室. 我们来说一下在Dat ...